func hoge<Foo: Protocol>(foo: Foo)と、fucn (foo: Protocol) だとstruct API { private init() { } struct Request { private init() { } struct GetDayOfWeekStories: APIRequest { (edited)case なし enum だとどうやってもインスタンス化できないので、 private init 書く必要ないから いいんじゃないかと思ってます。private init() { fatalError() }くらいにした方がいいかなと思ってました)/// Namespace for diagnostics enum diag {} extension diag { // メンバー群 ... 実際にはすべてのメンバーを extension に実装することにより、 case を書けなくするということをやっています。 (edited)??って、もしかしてコンパイルすごい遅いんでしょうか func apiErrorWithPromise(_ errorMsg: String? = nil, _ httpNumber: Int? = nil) -> Promise<Void>{ let errorMessage = "エラーメッセージ:\n"+(errorMsg ?? "不明なエラー(iMast)")+" ("+String(httpNumber ?? -1)+")\n\nエラーメッセージに従っても解決しない場合は、アプリを再起動してみてください。" return alertWithPromise( title: "APIエラー", message: errorMessage ) }var errorMessage:String = "" errorMessage = "エラーメッセージ:\n" errorMessage += errorMsg ?? "不明なエラー(iMast)" errorMessage += " (" errorMessage += String(httpNumber ?? -1) errorMessage += ")\n\nエラーメッセージに従っても解決しない場合は、アプリを再起動してみてください。" のようにしたら早くなりました、ありがとうございます\()どうも個人的に読みにくいんですよねlet errorMessage に +で連結して代入 let errorMsg2 = errorMsg ?? "不明なエラー(iMast)" let httpNumber2 = httpNumber ?? -1String.localizedStringWithFormat("", a, b) ) 今回の場合、これだけで速くなりそう。 (edited) typealias DataSearchFlags : oneof { None, Backward, Anchored }[default: ] を指定するのは、値の存在が保証されてない場面では??dict[key]! += 1 ってできたっけ?dict[key] += 1 ができるかどうかはわからないけど、 それができないからといってdict[key, default: never()] += 1 こう?dict[key, default: undefined()] += 1 var x: [String: Int] = [:] x["aaa"] = 3 x["aaa"]! += 1 print(x["aaa"])Optional(4)! 演算子はオペランドが左辺値の場合、左辺値のままにしてくれるのか。dict[key]! += 1 で良いってことになるのか・・・?dict[key] ?? defaultValue += 1dict[key] ||= 3 rubyだとこういうのよく見る (edited)func doubleQuestion( left: (read: ()->T?, write: (T)->Void), right: (read: ()->T, write: (T)->Void) ) -> (read: () -> T, write: (T) -> Void) (edited)Neverがボトムになれba まどろっこしい関数用意しなくて良くなるんだがな これおもしろいですね。dict[key, default: fatalError()] += 1 (edited)Never がボトムタイプでない現状だと、 func never<T>(_ message: String = "") -> T { fatalError(message) } みたいなのがいると。Never がボトムタイプにならないのって誰も Proposal 書かないから?An uninhabited type can be seen as a subtype of any other type ... This can be considered as a separate proposal.enum Never {} がボトムってどうなの?とか、他の enum Foo {} はどうなるの?とか、色々ややこしいから後回しにされてるのでは。+1 みたいなリプライあるけどwsomeFunc(AnyProtocol, parameter...) 的なのを考えてたんですが型で判別できるから、ありかどうかで迷ってる感じですdownload は Alamofire.download ですよね 正式にはSwift.min ですdownloadでアクセスできるので 結局そこがどうなのかって話なわけですよねcom.omochimetaru.Alamofire.download とかなってシンドイけど Alamofire.download ならまあ別に平気だし、だいたいは引数の型でも区別可能だし。download が出てきてごちゃごちゃしてきたときはfunc alamofireDownload() { .. } とかを自作してラップするのが良いんじゃないでしょうかimport ... as ... 的なやつですかprotocol APIRequestable { func load<R: Request>(request: R) -> R.Response } って感じですかね?requests<R: Request>(adapter: RequestAdapter=DefaultRequestAdapter()) -> (request: R) -> R.Response みたいにしようかで悩んでました (edited)swiftcが生成したhello_world.bcのターゲットがx86_64-apple-macosx10.9になってるからダメだよ、となってる。swiftcでtargetがasmjs-unknown-emscriptenな.bcを生成できないとダメなんじゃないかな。emscriptenのissueには「asmjs-unknown-emscriptenはWebAssembly(wasm32)とほぼ同じだよね?」って話してる人がいて、同じ人がSwiftをWebAssemblyへクロスコンパイルする話題をswift-devに投げてる。 https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20170626/004860.html (edited)Promise みたいに非同期処理とエラー処理をまとめて扱うような設計が嫌いで、非同期処理なら非同期処理、エラー処理ならエラー処理と責務を分解しておくべきだと考えて、エラー処理を含まない Promise ↓を作ってました。 https://github.com/koher/PromiseK/tree/dev-3.0Promise と Result が対応してるなと思って、それらを throws, try と (edited)async, await に対応させれば、自由にまぜて使えるよなぁと考えたものです。Promise にエラー処理を担当させちゃうと、 catch 忘れがおこっちゃうんですよねぇ。Promise と Result を一緒に使うとモナドパズルになっちゃうんですよねぇ。Result<Promise<Result<Foo>>> を Promise<Result<Foo>> にしたいとか。Array<Promise<Result<Foo>>> を Primise<Result<Array<Foo>>> にしたいとか。let a: Promise<Int> = ... let b: Promise<() throws -> Int> = a.map { try foo($0) } みたいに、 map 等の中で throw される場合は戻り値の型パラメータを () throws -> Foo に変換する API を考えてます。async, await ができれば無用の長物ですが。typealias Promise<Value> = (@escaping (Value) -> ()) -> () とする Promisure (= Promise + Closure) というライブラリも作ってましたw// Wrapping let a: Promise<Int> = { $0(3) } // like `Promise.resolve(3)` in JS // Promisifying a callback let b: Promise<Int> = promise { resolve in // Gets 5 after 1.0 seconds DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { resolve({ $0(5) }) } } // Operators let sum: Promise<Int> = a >>- { a in b >>- { b in { $0(a + b) } } } // flatMap let square: Promise<Int> = { $0 * $0 } <^> a // map let vector: Promise<Vector2> = curry(Vector2.init) <^> { $0(2.0) } <*> { $0(3.0) } // applyObservable にエラーが組み込まれてるのも僕は好みじゃないんですが、どうにかならないものでしょうか。throws, try みたいな特殊構文用意するよりも、モナドに対して do 記法用意するとかの方が汎用性はありますよねぇ。Result と Promise なら Promise が外側にいてほしいとか、モナド感での precedence みたいなものが考えられる気がして、Monad 型に対して演算子みたいな precedence を指定できてsort 関数みたいに、それを使って入れ子を関数が解決する。let a: Result<Promise<Int>> = ... let b: Promise<Result<Int>> = magic(a) do { let c: Int <-- b // 多段アンラップ let d: Result<Int> <- b // 一段アンラップ }try とかキーワード足さなくても自作モナドを組み込みモナドと同じように便利に使える。then が map と flatMap になってるんですけど、ネストしてくると読みづらいんですよねぇ・・・。Result を作らなかったから thorws, try を別の型でラップしたい場合に Promise<() throws -> Int> とかなるのはちょっとつらいですよね。Observable とかでやろうとしたらできないですよね?=> レベルだと演算子の方が可読性高そうだけど。Any でもダメで、任意の型パラじゃないといけないから辛い・・・extension Any { func `let`<T>(_ f: (Any) -> (T)) -> T { ... } }Any になっちゃってSelf にする extension ってできるんだっけ?public func =><T, U>(lhs: T, rhs: (T) throws -> U) rethrows -> U { return try rhs(lhs) }class Animal {} class Cat : Animal { func nya() { print("nyaa") } } extension Animal { func exec(f: (Self) -> Void) { f(self) } } let cat = Cat() cat.exec { $0.nya() } error: TempGround.playground:11:12: error: value of type 'Animal' has no member 'nya' cat.exec { $0.nya() } ^~ ~~~extension SomeProtocol where Self: SomeClass {} (edited)extension Any where Self: SomeClass {}protocol Anything {} extension Anything { func `let`<T>(_ f: (Self) throws -> T) rethrows -> T { return try f(self) } } extension Double: Anything {} (42.0).`let` { $0 + 1.0 / $0 }protocol Proto {} class Cat : Proto { func nya() { print("nyaa") } } extension Proto { func exec2(f: (Self) -> Void) { f(self) } } let cat = Cat() cat.exec2 { $0.nya() }=> の議論からするとそれは問題ないのでは=> の実装をオペレータからメソッドにできるかどうかだけを今考えていてAny が nominal でないってことだけなわけだ。Self を f に渡す形で実装はできる。Any じゃなくてLettable という protocol のメソッドとして作って.let を使いたい型については extension Cat : Lettable {} => の方がいいと思う。<$> は順番が逆転しちゃう問題もありますしねぇ・・・Promise のチェーンはpromise.flatMap { ... }.flatMap { ... }.flatMap { ... }primise >>- { ... } >>- { ... } >>- { ... }=> は標準ライブラリに入れてほしい・・・。r''' 出せなかったし嘘だと思うr''' は正規表現じゃなくて生マルチライン文字列Swift >>- で検索したら、一つ目は https://www.apple.com/jp/swift/ だけど、二つ目は typelift/Operadics で、三つ目は thoughtbot/Runes だった。<^> も駄目っぽいですよ>>= は重複してるから >>- に、 <$> は言語仕様上の制約で <^> になってる。haskell <$> は https://stackoverflow.com/questions/37286376/what-does-mean-in-haskell が出たからEither<T, Either<U, V>>public enum Either2<A, B> { case a(A) case b(B) } public protocol Either2Convertible { associatedtype CaseA associatedtype CaseB var asEither: Either2<CaseA, CaseB> { get } } extension Either2: Either2Convertible { public var asEither: Either2 { return self } }enum の抽象化ってことは、 Either2<Int, String> と Either2<String, Int> は区別したいってことですよね?Enum2, Enum2Convertible は良さそうEnum1 と Enum0 もいらないですか? Box と NeverEnum3 とかだと CaseA より Case1 とかの方がよさそうenum Enum2<T0, T1> { case Case0(T0) case Case1(T1) }EnumN とか?Houyhnhnm 音節Hou・yhn・hnm 発音記号/hwínəm|húːɪ‐/ 名詞可算名詞 フーイナム 《Swift 作 Gulliver's Travels (ガリバー旅行記)の中の人間的な理性が徹底している馬; 人間の形をした Yahooを支配する》. [馬の鳴き声からのSwiftの造語]swift package init したときに面倒$ swift package init --help OVERVIEW: Initialize a new package OPTIONS: --type empty|library|executable|system-moduleYahoo!の名前の由来は英語の「Yet Another Hierarchical Officious Oracle」(さらにもう一つの階層的でお節介な神託)の略だといわれている[57]。また、ファイロとヤンは自分たちのことを「ならずもの」だと考えているので、「粗野な人」という意味がある「Yahoo」(『ガリヴァー旅行記』に登場する野獣の名前が由来)という言葉を選んだと主張している[10]。さらに感嘆符が付いていることに関しては「ヤッホー!」「やったー!」を意味する英語の感動詞「yahoo」と掛けているとも考えられる。EnumNConvertible を EnumN に convert するんじゃないんですか?CustomStringConvertible とかを考えても EnumNConvertible とかならおかしくない気も。$ gyb -D Key=Value でパラメータ渡せるから、できるよpublic func animatedItems<S: AnimatableSectionModelType, O: ObservableType, C0: Reusable, C1: Reusable, C2: Reusable, C3: Reusable, C4: Reusable>(for type0: C0.Type, _ type1: C1.Type, _ type2: C2.Type, _ type3: C3.Type, _ type4: C4.Type) -> (O) -> Disposable where C0: UICollectionViewCell , C1: UICollectionViewCell , C2: UICollectionViewCell , C3: UICollectionViewCell , C4: UICollectionViewCell , S.Item: Enum5Convertible , S.Item.T0 == C0.Dependency , S.Item.T1 == C1.Dependency , S.Item.T2 == C2.Dependency , S.Item.T3 == C3.Dependency , S.Item.T4 == C4.Dependency , O.E == [S] { 使う側がこんな感じの地獄じみた形相になってるから、任意個数サポートできるのはもはや必須の感じがするdataSourceObservable .bind(to: tableView.rx.animatedItems(for: MyCell1.self, MyCell2.self)) .disposed(by: disposeBag)curry は昔ジェネリック引数14個(?)でコンパイル終わらなくなる問題がありましたが大丈夫そうですか?--line-directive= 入れないと ###sourceLocation が各行入るからなんなのかと思った!! オペレータ。機能は ! と同じ。Codableを適用すると、encode/decodeを実現するための実装をコンパイラーが生...struct ObservableA<T> { func subscribe(onNext:(T) -> () = { _ in }) { } } ObservableA<(Int, Int)>().subscribe(onNext: { (a, b) in // ok }) struct ObservableB<T> { func subscribe(onNext:((T) -> ())? = nil) { } } ObservableB<(Int, Int)>().subscribe(onNext: { (a, b) in // 死ーん })[omochi@omochi-iMac bin]$ pwd /Users/omochi/work/swift-source/build/Ninja-RelWithDebInfoAssert/swift-macosx-x86_64/bin [omochi@omochi-iMac bin]$ ./swift --version Swift version 4.0-dev (LLVM a15decabe3, Clang 60a12bf739, Swift fcd389b17e) Target: x86_64-apple-macosx10.9XCTestにXFAIL欲しいlitはテキストファイルならなんでも良いので、.mdにテストを書いたりできるのが面白いと思った。 https://github.com/apple/swift-integration-tests/blob/master/swift-package-init-exec.mdlit.cfg で指定すればですね。 https://github.com/apple/swift-integration-tests/blob/master/lit.cfg#L56XCTAssertEqualWithAcuracyはフィールドごとに確認するので増えるほど書くのがきつくなる stcuct Hoge { val a: Float } let x = Hoge(a: 0) let y = Hoge(a: 0) XCTAssertEqualWithAcuracy(x, y, accuracy: 1e-10) かといって関数に切り出すとXCodeで停止地点が分かりづらくなる。 XCTAsserEqualはEquatableを実装するだけで使えるので 同様にNealyEquatableみたいなプロトコルを切ってXCTestがそれを使うXCTAssertEqualWithAcuracyを用意してくれるのがベストかな。swift testとかで結構密接に結びついてるのでこちらからどうこうもしにくい気が。かといって関数に切り出すとXCodeで停止地点が分かりづらくなる。 切り出した関数に…file: StaticString = #file, line: UInt = #line)とかつけると良いのでは。QuickSpec が(間接的に) XCTestCase を継承してるのか。 https://github.com/Quick/Quick/blob/cd89737d3d5306124e91c357b66cde274a3269b4/Sources/Quick/QuickSpec.swift#L13swift testもできるのかな?swift testPackage.swiftはdependenciesがなんか複雑になってますね……Quick を使いたいからじゃない? < 複雑XCTestCase を継承してる https://github.com/Quick/Quick/blob/cd89737d3d5306124e91c357b66cde274a3269b4/Sources/QuickSpecBase/include/QuickSpecBase.h#L8testDependenciesがなかったので、テストの時だけ使う依存を実現してる。testDependencies 見た気がしてたんだけど使えなくなってるのか・・・Remove testDependencies from PackageDescription This feature was supposed to support dependencies only for the root package but at some point it stopped working however the API still remained open. This patch removes the public API. This is a valid and desired feature which is supposed to come back after it goes through proper review on swift evolution.
https://github.com/apple/swift-package-manager/commit/34b7826cb586b0769ea5f60a7718d7de599ce27fPackage.swiftを用意してたけど、環境変数で切り替えられることをikesyoさんに伝えたら使ってくれた。 (edited)Package の dependencies がテストからも使えるのなんか変な気も。> SWIFTPM_TEST_Carthage ああこれはSPMが定義してくれるのか。 Makefileで実行するときに設定してます。 https://github.com/Carthage/Carthage/blob/master/Makefile#L82Package.swiftがサンドボックス内で解釈される際に、SwiftPMがあえて環境変数を引き継ぐ様にしてるので、あながち間違いでもない。$ which -a sandbox-exec /usr/bin/sandbox-execDESCRIPTION The sandbox-exec command is DEPRECATED. Developers who wish to sandbox an app should instead adopt the App Sandbox feature described in the App Sandbox Design Guide.sandbox-execを使う様にしたコミットには環境変数がらみが特に書かれてないぽいから「環境変数をあえて渡す」というのは、チャットの中だけの話だったかも。#lineを使うってこれのことですか。 http://masilotti.com/xctest-helpers/ そもそもrecordFailureの存在を知らなくてログに行番号出るだけじゃ分かりにくいとか思っていました。XCTAssert*()シリーズにも渡せます。XCTAssert*()シリーズに渡せるってのは何でしょう? recordFailureがXCTestCaseのメソッドなのでXCTAssert*()のようにトップレベルのを定義するにはデフォルト引数で呼び出し元のselfを渡すとかするのかなぁと思ったのですが。selfを渡す話をよく覚えていない……XCTestCaseは引数にないのでどこから渡っているのか謎。 別の仕組みになっているんだろうか。recordFailureはXCTestCaseのメソッドなんですよ。 で、XCTAssert*()はトップレベルなのでrecordFailureが使えない(はず)XCTestCaseのextensionにすればいいんですが XCTAssert*()と同名を用いると例のバグ?でトップレベルのほうが見えなくなる。 (edited)recordFailure() ではなく XCTFail() を使ってました。 https://github.com/realm/SwiftLint/blob/master/Tests/SwiftLintFrameworkTests/IntegrationTests.swift#L35recordFailureを使うんじゃなくて内部でfile, line指定付きのXCTAssert*()を使えってことですね。 間違った方向に進んでました。enum Foo { static var foo: Foo { fatalError() } } func bar(_ foo: @autoclosure () -> Foo, ....) { ... } bar(.foo, ....) これが可能になるFoo.foo が実際には生成されないのがポイント?delegate.scrollView(scrollView, .didScroll) とかにいいかも (edited)struct Foo {} let foo = Foo() func bar(foo: Foo, ....) { ... } bar(foo, ....) そもそもこれじゃ駄目な理由は?struct Foo { static var foo = Foo() } ならまあ差はそれほど無いのかもしれない。.foo で書けるねstruct BarOverloads { static let foo = BarFoo() static let poo = BarPoo() } func bar(_ overload: BarFoo, ...) func bar(_ overload: BarPoo, ...) bar(.foo, ...) // できない bar(.poo, ...) (edited)struct Bar { struct Foo { static let foo = Foo() } struct Poo { static let poo = Poo() } } func bar(_ overload: Bar.Foo, ...) func bar(_ overload: Bar.Poo, ...) bar(.foo, ...) bar(.poo, ...) (edited)\ を使う感じで。 https://github.com/apple/swift-evolution/blob/master/proposals/0182-newline-escape-in-strings.md (edited)let s = """ ... In Windows you have paths like C:\ ... """\ を \\ に直さないといけないけど、新使用だとコンパイルエラーにならないから問題だと。特に """ だからといって文字列コピペしてきたときとかに問題起こりそう。c:\news とかもそうなんで。ちょっと弱い。Optionalのclosureでtupleが分解できない問題治してもらえた。はやいw
swift-4.0-DEVELOPMENT-SNAPSHOT-2017-07-11-a以降で直ってるぽいですね。struct でもサイズが大きいと勝手に最適化されたりするんでしょうか?どこかで触れられてましたっけ? https://developer.apple.com/videos/play/wwdc2017/402/std::string は確か COW だったけど COW 止めたんよね?Array 等の場合はマルチスレッドをサポートしないことで問題を回避している??var a = [2, 3, 5, 7] // スレッドA a[2] = 4 print(a) // スレッドB a[3] = 8 print(a) (edited)startThread( closure: () -> Void ) var a = [2, 3, 5, 7] a[2] = 4 startThread { a[3] = 8 }func startThread(f: @escaping () -> Void) { f() } func main() { var a = [2, 3, 5] a[0] = 88 startThread { a[1] = 99 } print(a) // [88, 99, 5] } main()struct である Array に包まれてるからvar a = 2 let foo: () -> () = { a += 1 } foo() print(a) // 3@escaping が@nonescaping なときはキャプチャされていてもvar a = 42 if random() < 0.0001 { foo = { print(a) } }foo がエスケープしてないからならない (edited)aはスタック上には無いと思いますvar foo: () -> () = {} func bar() { var a = 42 if random() < 0.0001 { foo = { print(a) } } } for _ in 1...10000000 { bar() }bar のコールのほとんどではスタックで済ませられるはずだけど、a[0] = 99 に、バッファストレージインスタンスの参照カウンタを見て、1ならそのまま書き換える、2以上ならディープコピーする、だから・・・a のスコープ自体がヒープに確保されてるから a のバッファへの参照カウントは常に 1 になりそう。 var a = [2, 3, 5, 7] // スレッドA startThread { a[2] = 4 print(a) } // スレッドB startThread { a[3] = 8 print(a) }a の実体が1つだけあってa[2] = 4 とかを、ユーザ側で ロックしないとバグりそう?a の実体が消えて、その先のストレージも消える。Array がスレッドアンセーフなのと、 COW がスレッドセーフかどうかと、参照カウンタがスレッドセーフなことは独立な気がする。Array がスレッドアンセーフな時点で Array の COW もスレッドセーフである必要はない?それとも、 Array がスレッドアンセーフな時点で Array の COW もスレッドセーフである必要はない? これがYESだと思います。Array 自体がスレッドアンセーフな時点で、 ArrayのCOWだけのスレッドセーフティーを考える意味がなくて、それもアンセーフArray のCOWに同期のオーバーヘッドはないと。std::string 自体はスレッドセーフじゃないと思うんだよなあArray をロックして使ってれっば問題は起こらない気がする。std::string が COW でなくなったのは別の理由なのかな?std::string と COW だった頃の std::string はまた別かもしれない・・・std::thread が入るときにこの規格がちゃんと整理されたって話がこの記事に同時に書いてあるのでstd::string としては、2つに見えてるんだ。ユーザには。std::string 一個単位で readonly safe ってことしか聞いてないから (というかコピーをまたいだグループでロックを取るなんで不可能)Array の COW はスレッドアンセーフでいい話と矛盾してない?var a = [2, 3, 5] // スレッドA a[2] = 4 // スレッドB var b = a b[2] = 999a と b で同期とるとか無理でしょってことか。var a = [2, 3, 5] // スレッドA startThread { a[2] = 4 } // スレッドB startThread { var b = a b[2] = 999 }Array 自体はスレッドアンセーフでも COW はスレッドセーフでないといけないってこと?a[2] = 4 と var b = a の2行を、ユーザがロックすべきだと思う。b = a の代入の最中に、 a[2] = 4 の処理が走り始めて内部状態が壊れる可能性があるはず。a[2] = 4 が書き込み操作で、 b = a の右辺はreadだから、競合してるのでロックが要るって事になるはず (edited)int の書き込みは atomic で同時なんてないんじゃないの? Q4. 基本型intはスレッドセーフですか? A4. 「同時アクセスが全て読み込み操作であれば安全」というスレッドセーフ性レベルが保証される。
http://yohhoy.hatenablog.jp/entry/2013/12/15/204116// 同一変数に対して片方が変更操作 int y = 2; void th1() { int r2 = y; // NG: データ競合 } void th2() { y = 42; // NG: データ競合 }th1 と th2 の中でロックしてても不定じゃない?struct Counter { var count = 0 mutating func countUp() { count += 1 } mutating func reset() { count = 0 } } (edited)structの場合CoWで実体分かれて問題ないんではreset の write の方struct だからといって CoW は自動的には起こりません。// 同一変数に対して片方が変更操作 int y = 2; void th1() { int r2 = y; // NG: データ競合 } void th2() { y = 42; // NG: データ競合 }int y = 2; void th1() { y = 99; } void th2() { y = 42; } の代入が atomic なら↑も OK となりそうだけど・・・。int の代入は atomic なのかint の代入がatomicでないから、ってことやんね?疑問は二つで、 - C++ において int の代入は atomic なのか - atomic だと仮定して↑はスレッドセーフなのかint にたいしてどれが適用されるかよくわからない。long と double 以外のプリミティブや参照の読み書きは atomic っぽいから、 int を同時に読み書きすること自体には問題なさそう。 For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write. Writes and reads of volatile long and double values are always atomic. Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
http://docs.oracle.com/javase/specs/jls/se8/html/jls-17.htmlint の読み書きが atomic かどうかを調べて atomic だと知って安心した記憶があるんだけど、 Java の話だったのかなぁ。その感覚で int の読み書きを atomic だと思っていたから↓がおかしいんじゃないかと思ったけど、 // 同一変数に対して片方が変更操作 int y = 2; void th1() { int r2 = y; // NG: データ競合 } void th2() { y = 42; // NG: データ競合 } C++ では規格上 atomic でないからたとえそれを atomic に行う CPU 向けにコンパイルしたとしてもどのように最適化されるかわからず未定義動作なので↑は正しい、と。var a = 3 var b = 2 a = 4 b = 1 print("a=\(a), b=\(b)") (edited)Int 等の代入や、参照型のアドレスの代入が atomic なのか気になる。a = 3 や b = 2 の時間帯は削除されるんじゃないかなあ。Int やアドレスの代入が atomic でないなら、結構気を付けてプログラム書かないといけないなぁInt の read / write ですら atomic ではない?.swift か。Int じゃ駄目 (edited)Int であってもlet a = _stdlib_AtomicInt(42)let a = _stdlib_AtomicInt(42) print(a.load()) // 42 a.store(999) print(a.load()) // 999_stdlib_AtomicInt とかも atomic であるところまでの保証であって「「Intの代入がatomic」だとしても」は同じじゃないの?var x = 3 x = 2func a() -> Int { var a = 3 a = 2 return a }func a の例で言うなら、これってfunc a() -> Int { return 2 } と最適化されるかもしれないfunc a(x: inout Int) -> Int { x = 3 x = 2 return x } (edited)func a(x: inout Int) -> Int { return 2 } (edited)std::atomic<int> に対してはstore 命令の削除も禁止されるはず。x → a だったら inout だから x への代入は必須じゃない?func a(x: inout Int) -> Int { x = 2 return 2 } (edited)x = 3 の瞬間が削除されていることとreturn x じゃなくて return 2 になっていることがポイントx=3 が観測できないし、 どんだけ x に書き込んでも、 固定で 2 が返ってくるx = 3 が削除されるのは問題になりうる。a が atomic な挙動をしているだけだと思うんだけど、 (edited)int data = 0; volatile int flag = 0; // 生産側スレッド void producer_thread() { data = 42; // [A] flag = 1; // [B] ??? } // 消費側スレッド void consumer_thread() { while ( !flag ) {} // [C] assert(data == 42); // [D] ??? } (edited)func a() { lock.sync { atomicFoo(42) } } func b() { lock.sync { atomicFoo(999) } } の lock はいらないよね?ということでdata を読んだ時に、0か42以外の中途半端にビットが書き換わった変な数字になったり、CPUがぶっ壊れたりしない、としてもfunc a() { lock.sync { atomicFoo(42) } } func b() { lock.sync { atomicFoo(999) } } これ自体はlockはいらない。Int を同時に更新する(だけ)みたいなのでも同期が必要となると想定していないケースが多そうということです。lock が不要だったらうれしい。 var x: Int = 42 func a() { lock.sync { x = 999 } } func b() { lock.sync { x = 888 } } (edited)let x = LargeSizeValue() f1(x) f2(x) f3(x)let だからそのまま渡せるのかfunc consume1(_ b: BigStruct) { consume2(b) } func consume2(_ b: BigStruct) { consume3(b) } func consume3(_ b: BigStruct) { consume4(b) } func consume4(_ b: BigStruct) { print(b) } 極端だけどこういう場合。 (edited)func consume1(_ b: P) { consume2(b) } func consume2(_ b: P) { consume3(b) } func consume3(_ b: P) { consume4(b) } func consume4(_ b: P) { print(b) } extension BigStruct: P {} こうしたほうが速そう。結局のところ Swift4 の Existential CoW はヒープに 25 バイト以上のデカい構造体を alloc/initialize/dealloc するより参照カウンタ操作の方が速いってだけなのかな? は Swift 3 vs Swift 4 の話ではない??func consume1<T: P>(_ b: T) { consume2(b) } func consume2<T: P>(_ b: T) { consume3(b) } func consume3<T: P>(_ b: T) { consume4(b) } func consume4<T: P>(_ b: T) { print(b) } extension BigStruct: P {}// 1.swift protocol Hogeable { func hoge() -> Int } struct HogeCat: Hogeable { func hoge() -> Int { return 1111 } } func callHogeableHoge(hogeable: Hogeable) -> Int { return hogeable.hoge() } func callCatHoge(cat: HogeCat) -> Int { return callHogeableHoge(cat) } print(callCatHoge(HogeCat()))callHogeableHoge nofunc foo(_ a: P, _ b: P) { ... } extension Int: P {} extension String: P {} foo(2, 3) // スペシャライズされる foo(2, "abc") // スペシャライズされない@_specialize 付けなくてもスペシャライズって起こるんだっけ?func consume1(_ b: P) { consume2(b) } func consume2(_ b: P) { consume3(b) } func consume3(_ b: P) { consume4(b) } func consume4(_ b: P) { print(b) } extension BigStruct: P {} こうしたほうが速そう。 ↑これでいけそうですfunc consume1(_ b: shared BigStruct) { } void consume1(const BigStruct & b) と同じ意味func consume1(_ b: inout BigStruct) { consume2(&b) } func consume2(_ b: inout BigStruct) { consume3(&b) } func consume3(_ b: inout BigStruct) { consume4(&b) } func consume4(_ b: inout BigStruct) { print(b) } extension BigStruct: P {} (edited)var x = BigStruct() compose(consume1(&x), consume1(&x))std::atomic で実現されてました。 struct HeapObject に フィールド InlineRefCounts refCounts がある。 https://github.com/apple/swift/blob/b7d78853112c1279fc7bc5b85853779040f13703/stdlib/public/SwiftShims/HeapObject.h InlineRefCounts は RefCounts<InlineRefCountBits> のエイリアス class RefCounts<T> は フィールド std::atomic<T> を持ってる。 デクリメント処理はなんかいろいろあるけど doDecrement が基本的なやつっぽくて、 分岐もいろいろ複雑なんだけどstd::atomicのcompare_exchange_weakを呼び出してたりする。 InlineRefCountBits は RefCountBitsT<RefCountIsInline> のエイリアス RefCountIsInline は true な定数 RefCountBitsTはカウンタに加えて動作に関するビットが5こぐらいくっついたよくわからんやつ。 https://github.com/apple/swift/blob/b7d78853112c1279fc7bc5b85853779040f13703/stdlib/public/SwiftShims/RefCount.hthe-sharing-economy wSwift Static Analysis@Apple Cupertino, CA
https://twitter.com/CodaFi_Math@CMU 2019 どういうこと?未来人??val ( Swift の let に相当)で interface のプロパティ宣言して、具象クラス側で Computed property にできるよね・・・。mutating を一つももたないクラスはイミュータブルクラスになる。 mutable class Foo { func bar() -> Bar {} mutating func baz() -> Baz {} } (edited)mutating を作ろうとするとコンパイルエラー。mutating みたときは、これはもしかして?と思ったけどちょっと違った。けど、結局値型中心で考える方がセンスがいいと思う。pure について言及されてて、 D 言語はよく知らないですが、、 pure な方がデフォルトであるのがいいと思うのと、これを型にも広げられないんだったら効果半減だと思います。let なプロパティを Computed Property としてオーバーライドできないという制約はあるから、「もともと let な stored property だったものを、後から computed property の getter に差し替えても」動くとはいえ意図的に区別して制約が課されてるんじゃないかな?interface A { val value: Int } class A1: A { override val value: Int get() = A1.getCurrent() companion object { var value: Int = 0 fun getCurrent(): Int { value += 1 return value } } } fun main(args : Array<String>) { val a1 = A1() println(a1.value) println(a1.value) }
let in protocols https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170619/037676.htmlprotocol で nested type 作れないのって何か理由があるんですっけ?名前空間的に、その protocol でしか使わない型をネストしたい・・・。protocol Foo { var bar: Bar { get } struct Bar { ... } } みたいな (edited)protocol の内側に static func をかいたとき、それは、 Foo.hoge() と呼び出せるわけじゃなくてFoo に関連したいくつかのパラメータをまとめた Bar を作りたいけど、 Bar は Foo から独立したら意味を持たない型なのでネストしたいって感じです。struct Bar<F: Foo> を作るのが筋良さそうprotocol Foo { typealias Bar = FooBar var a: Bar { get } } struct FooBar { } struct FooImpl: Foo { var a: Foo.Bar }Foo の異なる具象型間で Bar をやりとりできないですよね?_FooBar にしておけばありかもしれないなぁ。 1> protocol Foo { 2. typealias Bar = FooBar 3. var a: Bar { get } 4. } 5. 6. struct FooBar { 7. 8. } 9> let bar: Foo.Bar = Foo.Bar() error: repl.swift:9:27: error: cannot call value of non-function type 'Foo.Bar' (aka 'FooBar') let bar: Foo.Bar = Foo.Bar() ~~~~~~~^~typealias はデフォルトではないのでは?associatedtype にしてデフォルト設定するんじゃないっけ? = で指定しておいた型になるんじゃなかったっけstruct FooBar {} protocol Foo { associatedtype Bar } extension Foo { typealias Bar = FooBar }struct FooBar {} protocol Foo { typealias Bar = FooBar }struct P0Param {} protocol P0 { typealias Param = P0Param var a: Param { get set } } var a = P0.Param() print(a) 1> protocol Foo { 2. typealias Bar = FooBar 3. var a: Bar { get } 4. } 5. 6. struct FooBar { 7. 8. } 9> let bar: Foo.Bar = Foo.Bar()
typealias in protocolswift-4.0-branch-07-11-2017 https://github.com/apple/swift/tree/swift-4.0-branch-07-11-2017 みたいなブランチからビルドされてたぽいだけど、beta 5はどこからビルドされているのだろう? (edited)swift-4.0-branch-07-11-2017 みたいなスナップショットが出ると、discordで報告していて、swift-4.0-DEVELOPMENT-SNAPSHOT-2017-08-02-a以降 swift-4.0-DEVELOPMENT-SNAPSHOT-2017-08-04-aより前 ぽい。 (edited)Copy-On-Write (🐮) types like Array and String are sharable リファレンスカウントとCOWはスレッドセーフだけど Array そのものの操作はスレッドセーフではない?async, await 出す前に来てしまった・・・。Concurrency is a broad and sweeping concept that can cover a wide range of topics. To help scope this down a bit, here are some non-goals for this proposal: - We are focusing on task based concurrency, not data parallelism. This is why we focus on GCD and threads as the baseline, while completely ignoring SIMD vectorization, data parallel for loops, etc. - In the systems programming context, it is important for Swift developers to have low-level opt-in access to something like the C or C++ memory consistency model. This is definitely interesting to push forward, but is orthogonal to this work. - We are not discussing APIs to improve existing concurrency patterns (e.g. atomic integers, better GCD APIs, etc).UIImage(named:) が挙げられてるんだけど、このレベルの API から非同期化するつもりなのかな? C# の世界みたいになりそう。async が良さそう。throws って言ってるのと同じ気がする。can of worms って出てきてなんだ?と思ったら↓のような意味の表現らしい。 UIImage(named:) にまで踏み込むなら、 pure Swift UI ライブラリもワンチャンあるかも?you should only have to teach your types how to serialize/🐟 themselvesfish なんだよなあSpeaking of reliable systems, introducing an actor model is a good opportunity and excuse to introduce a mechanism for handling and partially recovering from runtime failures (like failed force-unwrap operations, out-of-bounds array accesses, etc). We explore several options that are possible to implement and make a recommendation that we think will be a good for for UI and server applications.async / await まで来た。async と、呼び出しの await は (Int) -> Int // #1: Normal function (Int) throws -> Int // #2: Throwing function (Int) async -> Int // #3: Asynchronous function (Int) async throws -> Int // #4: Asynchronous function, can also throw.reasync がなさそうだから swift-evolution に書いておくか。await! もほしいな。! を使うのが良いのか自信がない。 actor TableModel { let mainActor : TheMainActor var theList : [String] = [] { didSet { mainActor.updateTableView(theList) } } init(mainActor: TheMainActor) { self.mainActor = mainActor } // this checks to see if all the entries in the list are capitalized: // if so, it capitalize the string before returning it to encourage // capitalization consistency in the list. func prettify(_ x : String) -> String { // ... details omitted, it just pokes theList directly ... } actor func add(entry: String) { theList.append(prettify(entry)) } } reliable actor Notifier { ... } reliable actor class Notifier { ... } distributed actor MyDistributedCache { ... } distributed actor class MyDistributedCache { ... }func queueHopping() async -> Void { doSomeStuff() await DispatchQueue.main.syncCoroutine() doSomeStuffOnMainThread() await backgroundQueue.asyncCoroutine() doSomeStuffInBackground() }/// Begins an asynchronous coroutine, transferring control to `body` until it /// either suspends itself for the first time with `suspendAsync` or completes, /// at which point `beginAsync` returns. If the async process completes by /// throwing an error before suspending itself, `beginAsync` rethrows the error. func beginAsync(_ body: () async throws -> Void) rethrows -> Void ↑の戻り値ってなんで T じゃないの?@IBAction func buttonDidClick(sender:AnyObject) { // 1 beginAsync { // 2 let image = await processImage() imageView.image = image } // 3 }rethrows はどこで活躍するの?If the async process completes by throwing an error before suspending itself, `beginAsync` rethrows the error.suspendAsync は func suspendAsync<T>( _ body: (_ continuation: @escaping (T) throws -> ()) -> () ) async rethrows -> T じゃないんだ??throws の位置がおかしいかfunc suspendAsync<T>( _ body: (_ continuation: @escaping (T) -> ()) throws -> () ) async rethrows -> T// Legacy callback-based API func getStuff(completion: (Stuff) -> Void) { ... } // Swift wrapper func getStuff() async -> Stuff { return await suspendAsync { continuation in getStuff(completion: continuation) } }asynchronize が間違ってるっぽいな。func suspendAsync<T>( _ body: (_ continuation: @escaping (T) -> ()) -> ()) async -> T func asynchronize(_ operation: ((T) throws -> ()) -> ()) async rethrows -> T // ラベル等を消すと func suspendAsync<T>( _ body: ((T) -> ()) -> ()) async -> T (edited)(T) throws -> () があるけど、() throws -> T じゃないと。func asynchronize(_ operation: ((() throws -> T) -> ()) -> ()) async rethrows -> Tfunc suspendAsync<T>( _ body: (_ continuation: @escaping (() throws -> T) -> ()) -> ()) async -> Treturn await suspendAsync { continuation, error in awaiters.append({ switch $0 { case .error(let e): error(e) case .value(let v): continuation(v) } }) } が return await suspendAsync { continuation in awaiters.append({ switch $0 { case .error(let e): continuation { throw e } case .value(let v): continuation { v } } }) } になる。pass みたい。async await じゃなくて yields と yield にするって選択肢についても書かれてますねasync / await は必ずしも意味が正しくないんよね。async(nonthrowing) を導入するasync(nonthrowing) より async nonthrows とかの方がいいな。 nonthrows は英語的におかしそうだけど。nothorw があるよthrows がよくなかった気がするな。 Java のせいだけど。throwing func とか async func とかの方が英語的によかったかも。 (edited)nonthrowing func だったら nonmutating func とかとも整合するし。do { let a = await foo() let b = await bar(a) ... } waitreasyncawait! もいいけど、まとめてブロックも必要かと。Blocking calls Affordances could be added to better call blocking APIs from async functions and to hard wait for an async function to complete. There are significant tradeoffs and wide open design space to explore here, and none of it is necessary for the base proposal.let a = await foo() let b = await bar() のときに foo と bar を並列に走らせるのか直列に走らせるのかはどうだろう?func processImageData1a() async -> Image { let dataResource = Future { await loadWebResource("dataprofile.txt") } let imageResource = Future { await loadWebResource("imagedata.dat") } // ... other stuff can go here to cover load latency... let imageTmp = await decodeImage(dataResource.get(), imageResource.get()) let imageResult = await dewarpAndCleanupImage(imageTmp) return imageResult }beginAsync していてget() async throws -> T なのでasync/awai に続いて Typed throws も。 Error が existential だからパフォーマンスが、ってことが書かれていて興味深い。One thing that I’m personally very concerned about is in the systems programming domain. Systems code is sort of the classic example of code that is low-level enough and finely specified enough that there are lots of knowable things, including the failure modes. Beyond expressivity though, our current model involves boxing thrown values into an Error existential, something that forces an implicit memory allocation when the value is large. Unless this is fixed, I’m very concerned that we’ll end up with a situation where certain kinds of systems code (i.e., that which cares about real time guarantees) will not be able to use error handling at all.JohnMC has some ideas on how to change code generation for ‘throws’ to avoid this problem, but I don’t understand his ideas enough to know if they are practical and likely to happen or not. で、 Chris Lattner のこのメールに John McCall が返信してるから、それ見たらわかるかも。This combination of requirements means that all operations must be implicitly "unwindable" starting from almost any call site it makes. For the stability of the system, this unwinding process must restore any invariants that might have been temporarily violated; but the compiler cannot assist the programmer in this. The programmer must consciously recognize that an error is possible while an invariant is broken, and they must do this proactively --- that, or track it down when they inevitably forget. This requires thinking quite rigorously about one's code, both to foresee all the error sites and to recognize that an important invariant is in flux. How much of a problem this poses depends quite a lot on the code being written. There are some styles of programming that make it pretty innocuous. For example, a highly functional program which conscientiously kept mutation and side-effects to its outermost loops would naturally have very few points where any invariants were in flux; propagating an error out of an arbitrary place within an operation would simply abandon all the work done up to that point. However, this happy state falls apart quite quickly the more that mutation and other side-effects come into play. Complex mutations cannot be trivially reversed. Packets cannot be unsent. And it would be quite amazing for us to assert that code shouldn't be written that way, understanding nothing else about it. As long as programmers do face these issues, the language has some responsibility to help them.More generally, by modeling both `throws` and `async` as effects on function types, we can eventually provide common abstraction tools to abstract over both effects in protocols and generic code
https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619#rethrows-could-be-generalized-to-support-potentially-async-operationseffects ってのが専門用語を匂わせてる気がするawait try と書いてたけど、プロポーザル通り try await じゃないといけない気がしてきた。try も await も flatMap と等価でモナドを剥がすわけだけど、 async throws は Promise が外側だからまず Promise を剥がさないといけず、その後 Result を剥がすと考えると try await じゃないとおかしい。beginAsync の rethrows と body の throws ってやっぱダメじゃない?I agree. I think `rethorws` for `beginAsync` is problematic. For example, what happens when the `foo` in the following code throws an `Error` asynchronously? func foo() async throws { ... } beginAsync(foo) `foo` is acceptable as `beginAsync`'s `body` by its type. However its error might be thrown asynchronously and it is impossible to rethrow it. So the error must be ignored or treated as an universal error by untyped propagation. It breaks type safety about error handling. So I think the signature of `beginAsync` should be the following one. func beginAsync(_ body: () async -> Void) -> VoidbeginAsync rethrows the error.await! についても違う方向で話されてたから blocking のことに言及してみた。 https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170814/039096.htmlasync(nonthrowing) おもしろいけど、それをやっちゃうと throws, async に続く第三のエフェクトを導入したときに整合性をとるのに困ると思う。async/await の Proposal と同じような内容が話されてる。 https://gist.github.com/koher/5cd16adac7a62b6d3eb0b910ccc13534import Accelerate extension Array where Element: Comparable { mutating func clip(low: Element, high: Element) { print("normal version") for i in 0..<self.count { self[i] = Swift.max(Swift.min(self[i], high), low) } } @_specialize(Float) func clipped(low: Element, high: Element) -> [Element] { var ret = self ret.clip(low: low, high: high) return ret } } extension Array where Element == Float { mutating func clip(low: Float, high: Float) { print("accelerate version") var low = low var high = high self.withUnsafeMutableBufferPointer { bp in let p = bp.baseAddress! vDSP_vclip(p, 1, &low, &high, p, 1, vDSP_Length(bp.count)) } } } var int = (0..<10).map { Int($0) } int.clipped(low: 3, high: 5) // normal version int.clip(low: 3, high: 5) // normal version var x = (0..<10).map { Float($0) } x.clipped(low: 3, high: 5) // normal version x.clip(low: 3, high: 5) // acceerate version
Float用のclippedを多重実装せずにaccelerate versionを呼べないかと思ってるんですが@_specializeでもだめっぽい…… (edited)@_specialize ってそのメソッドや関数の型パラメータに利くんじゃないっけ?private func 化して@_specialize 付けるとか?protocol ArrayClippable を用意してprotocol ArrayClippable { static func clipArray(_ array: inout Array<Self>, min: Self, max: Self) } extension ArrayClippable { static func clippedArray(_ array: Array<Self>, min: Self, max: Self) -> Array<Self> { var ret = array Self.clipArray(&ret, min: min, max: max) return ret } } extension Array where Element : ArrayClippable { mutating func clip(min: Element, max: Element) { Element.clipArray(&self, min: min, max: max) } func clipped(min: Element, max: Element) -> Array<Element> { return Element.clippedArray(self, min: min, max: max) } }extension Comparable : ArrayClippable { static func clipArray(_ array: inout Array<Self>, min: Self, max: Self) { // ... } }@_specialize で呼び分けるのは無理なんじゃないかな?Playground execution failed: error: MyPlayground.playground:10:1: error: extension of protocol 'Comparable' cannot have an inheritance clause extension Comparable: ArrayClippable { ^ ~~~~~~~~~~~~~~ つけられない?@_specialize について、 _ で始まるものは本当に使わないほうがいいです。意図的にやっているのかっていうくらい頻繁に仕様が変わります。 by @rintarostruct S<T> { var x: T @_specialize(where T == Int, U == Float) mutating func exchangeSecond<U>(_ u: U, _ t: T) -> (U, T) { x = t return (u, x) } }_ に限らず破壊的変更がどのみち入るから(struct S<T> { var x: T mutating func exchangeSecond<U>(_ u: U, _ t: T) -> (U, T) { x = t return (u, x) } } extension S where T == Float { mutating func exchangeSecond(_ u: Int, _ t: Float) -> (Int, Float){ x = t return (u, x) } }@_specialize をなくせば動きはするしね。@_specialize では無理だと思う。MyArrayとMyArray2なんですが、同じテストコードでモジュール外にあるMyArrayの方が数百倍遅くて原因が分かりません。 モジュールまたぐ時ってwhole module optimization以外何かありましたっけ?makeIteratorはCollectionに生えているのが使えるのでMyArrayとおなじところにMyFloatArrayを作ってためしましたArrayに詰めたいけれど型が不定だと初期値をどうするのやらFloat に対して付けるんじゃないの?import Foundation var data = Data(bytes: [0x50, 0x4B, 0x01, 0x02, 0x41, 0x61]) data.removeFirst() print(data.startIndex) // swift-3.1: "0\n", swift-4.0: "1\n" data.customMirror // crash on swift-4.0 https://bugs.swift.org/browse/SR-5811 (edited)self[0..<nBytes]ってやってるからw https://github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/Data.swift#L1773 (edited)Data の件、 Array と挙動が違って混乱を招くかも? Welcome to Apple Swift version 4.0 (swiftlang-900.0.59 clang-900.0.34.2). Type :help for assistance. 1> var array = [2, 3, 5] array: [Int] = 3 values { [0] = 2 [1] = 3 [2] = 5 } 2> print(array.startIndex) 0 3> array.removeFirst() $R0: Int = 2 4> print(array.startIndex) 0subscript の index は startIndex が最初の要素を指すよ。 1> import Foundation 2> var data = Data(bytes: [2, 3, 5]) 3> print(data.startIndex) 0 4> data.removeFirst() $R0: UInt8 = 2 5> print(data.startIndex) 1 6> print(data[1]) 3 (edited)Array は必ず startIndex が 0 で endIndex が count - 1 として、わざわざ ArraySlice という型を設けて分離しているのに対して Data は一つの型でやってしまっていることとremoveFirst が startIndex に影響を及ぼすのか及ぼさないのかという仕様の違いをどっちも混ぜてしまったのが問題では。 1> var ns: ArraySlice<Int> = [2, 3, 5] ns: ArraySlice<Int> = 3 values { [0] = 2 [1] = 3 [2] = 5 } 2> print(ns.startIndex) 0 3> ns.removeFirst() $R0: Int = 2 4> print(ns.startIndex) 1ArraySlice とは一貫してるのかData は最初からスライスだと考えれば良いのか。Data はそういうのとは違うからInt で適切だと思う。String より遅いけど、計算量のオーダーは同じ。String インスタンスを作るのと大して変わらない。String から構築してるからString がヒープに確保してるでっかい領域をCharacter が参照してるだけになるのでは?String から characters を取り出したときに内部的にどうなってるか詳しいことはしらない。String より EasyText の Text の方が速そう。String がややこしいこと考えずに書記素クラスタで扱えるのはとても良いと思う。$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> : (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. >>> [x * x for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> [omochi@omochi-iMac SwiftSyntax (master *=)]$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> : (lldb) script Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D. >>> 3 + 3 6 >>> exit (lldb) ^D 1> ^D>>> ^D (lldb) ^D 1> ^D $ var exit: Never みたいなの生やせばexitで抜けれるようになるかしら$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> import Darwin 2> exit(0) REPL requires a running target process. error: REPL process is no longer alive, exiting REPL なら抜けられる。 (edited)struct Hoge { var foo: Int = 0 var bar: Int = 0 } var hoge: Hoge = Hoge() { didSet { print("didSet") } } みたいな、監視されている struct 値を更新するときに hoge.foo = 12 hoge.bar = 13 (edited) _ = { $0.foo = 12 $0.bar = 13 }(&hoge) っていう方法よりシンプルでわかりやすいのってありますか?(hoge.foo, hoge.bar) = (12, 13) これもだめ hoge.mutateメソッドを生やしたら少し分かりやすいかも?protocol Mutable { } extension Mutable { mutating func mutate(_ f: (inout Self)->Void) { f(&self) } } struct Hoge: Mutable { var foo: Int = 0 var bar: Int = 0 } (edited) hoge.mutate { $0.foo = 12 $0.bar = 13 } いいかも、使わせていただきまっす!Mutableプロトコルは名前が不自然なのでそこだけ変えて下さいwRxSwift.Variable に使いたかっただけなのでこんな感じにしました。 extension RxSwift.Variable { func mutateValue(fn: (inout Element) -> Void) { fn(&value) } }variable.value.foo = xxx をマルチスレッドでやらかすと死ねるdidSetの場合もですけど冪等に作っておいて多重実行は気にしないのが良いんですかね……extension RxSwift.Variable { func mutateValue(fn: (inout Element) -> Void) { _lock.lock() fn(&value) _lock.unlock() } } できるかな?こんな感じが良さそう。[T1: BinaryFloatingPoint]を[T2: BinaryInteger]に変換するメソッドが書けるようになって喜んでたら [T1: BinaryInteger]を[T2: BinaryFloatingPoint]に変換するほうができなかったSignedIntegerとUnsignedIntegerに分けてそれぞれInt64, UInt64を経由して変換するというろくでもない方法に落ち着いてしまったfatal error: Array<Foo> does not conform to Encodable because Pedestrian does not conform to Encodable.: (edited)Countable...Range を一掃できるのもうれしい。Codable のエラーメッセージがどうなってるかは知らないけど、原理的にはパスをたどれそうな気がする。エラーが throw されてきたら素通しにせず、一度 catch してからエラーメッセージにパスを追記して throw しなおせばいいんじゃない?そうなってなかったら swift リポジトリへの PR チャンスかも?Encoder, Decoderのエラーに含まれるパスは「とりあえずあるレベル」だと記憶。 (edited)Decoder につけなきゃいけないのか。=> の inout 版がほしい気がしてきた。func =><T>(lhs: T, rhs: (inout T) throws -> ()) rethrows -> T { var value = lhs try rhs(&value) return value } (edited)func =><T, U>(lhs: T, rhs: (T) throws -> U) rethrows -> U { return try rhs(lhs) }var teamToCount: [String: Int] = [:] for user in users { let team = user.team if let count = teamToCount[team] { teamToCount[team] = count + 1 } else { teamToCount[team] = 1 } } を、もし inout 版の => があれば reduce(into:_:) を使わなくても let teamToCount: [String: Int] = [:] => { teamToCount in for user in users { teamToCount[user.team, default: 0] += 1 } } って書けそうだなぁと。Codable など劇的にコーディングが楽になる新機能だと思いますが、ちょっとした便利な小技もあります。 そんな、 Swift 4 の小技の魅力の一面を 3 行...infix operator => : SwifletPrecedence precedencegroup SwifletPrecedence { higherThan: AssignmentPrecedence associativity: left } func =><T>(lhs: T, rhs: (inout T) throws -> ()) rethrows -> T { var value = lhs try rhs(&value) return value } print([Int]() => { (a: [Int]) -> () in a.append(42) })
$ swift inout-let.swift inout-let.swift:14:40: error: value of type 'Any' has no member 'append' print([Int]() => { (a: [Int]) -> () in a.append(42) }) ^ ~~~~~~ inout-let.swift:14:40: note: cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members print([Int]() => { (a: [Int]) -> () in a.append(42) }) ^ ( as AnyObject) (edited){ (a: inout [Int]) -> () in a.append(42) } クロージャパラメータに inout が必要inout がいるんですね・・・。考えてみれば当たり前だ。infix operator => : SwifletPrecedence precedencegroup SwifletPrecedence { higherThan: AssignmentPrecedence associativity: left } func =><T>(lhs: T, rhs: (inout T) throws -> ()) rethrows -> T { var value = lhs try rhs(&value) return value } print([Int]() => { (a: inout [Int]) -> () in a.append(42) }) print([Int]() => { a in a.append(42) }) print([Int]() => { $0.append(42) })
$ swift inout-let.swift inout-let.swift:15:25: error: value of type 'Any' has no member 'append' print([Int]() => { a in a.append(42) }) ^ ~~~~~~ inout-let.swift:15:25: note: cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members print([Int]() => { a in a.append(42) }) ^ ( as AnyObject) inout-let.swift:16:20: error: value of type 'Any' has no member 'append' print([Int]() => { $0.append(42) }) ^~ ~~~~~~ inout-let.swift:16:20: note: cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members print([Int]() => { $0.append(42) }) ^ ( as AnyObject)$ swift --version Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36) Target: x86_64-apple-macosx10.9reduce(into:_:) だとうまく推論できたのも謎。// 型の準備 struct User { var team: String } // 値の準備 let users = [ User(team: "A"), User(team: "B"), User(team: "B"), User(team: "A"), User(team: "C"), User(team: "A"), User(team: "C"), User(team: "B"), User(team: "D"), User(team: "A"), ] // 集計 let teamToCount: [String: Int] = users.reduce(into: [:]) { teamToCount, user in teamToCount[user.team, default: 0] += 1 } // 出力 for (team, count) in (teamToCount.sorted { $0.key < $1.key }) { print("\(team): \(count)") }teamToCount が inout なんですが、問題なく通ります。reduce(into:_:) と違うのは inout の後の第二引数があるかないかくらいな気がするけど・・・ func parseImportDecl(tokens: ArraySlice<TokenSyntax>) throws -> (decl: ImportDecl, rest: ArraySlice<TokenSyntax>)? { var index = tokens.startIndex ... return (decl: ImportDecl(keywordIndex: keywordIndex - tokens.startIndex, nameIndex: nameIndex - tokens.startIndex, tokens: Array(tokens[..<index])), rest: tokens[index...]) } (edited)echo 'Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated())'|TOOLCHAINS=swift swiftc -でswiftcがクラッシュする。export TOOLCHAINS=org.swift.3020170918aDictionary(uniqueKeysWithValues: ["a","b","c"].enumerated().filter { $1 != "a" }) もクラッシュ。Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated().filter { $1 != "a" }.map { ($0, $1) }) でクラッシュ回避。.map { $0 } でもいけますね 3> Dictionary(uniqueKeysWithValues: [(offset: 0, element: "a")]) $R2: [Int : String] = 1 key/value pair { [0] = { key = 0 value = "a" } }Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated().lazy) は別の形でクラッシュecho 'Dictionary(uniqueKeysWithValues: ["a","b","c"].enumerated())'|TOOLCHAINS=swift swiftc -でswiftcがクラッシュする。
swift-DEVELOPMENT-SNAPSHOT-2017-09-28-aで直った。[swift-evolution] Idea: Public Access Modifier Respected in Type Definition The core team would only consider a refinement or change to access control if there were something actively broken that mattered for ABI stability. -Chris$ swift Welcome to Apple Swift version 4.0 (swiftlang-900.0.63.10 clang-900.0.36). Type :help for assistance. 1> class Animal {} 2> class Cat: Animal {} 3> let a: () -> Cat = { Cat () } a: () -> Cat = 0x00000001000c5590 $__lldb_expr7`closure #1 () -> __lldb_expr_3.Cat in __lldb_expr_6 at repl.swift:3 4> let b: () -> Animal = a b: () -> Animal = 0x00000001000c5590 $__lldb_expr7`closure #1 () -> __lldb_expr_3.Cat in __lldb_expr_6 at repl.swift:3 5> let c: () -> [Cat] = { [Cat()] } c: () -> [Cat] = 0x00000001000c5680 $__lldb_expr11`closure #1 () -> Swift.Array<__lldb_expr_3.Cat> in __lldb_expr_10 at repl.swift:5 6> let d: () -> [Animal] = c error: repl.swift:6:25: error: cannot convert value of type '() -> [Cat]' to specified type '() -> [Animal]' let d: () -> [Animal] = c ^ 6> let e: [Cat] = [Cat()] e: [Cat] = 1 value { [0] = { __lldb_expr_1.Animal = {} } } 7> e is [Animal] $R0: Bool = trueis は 真になるのに、関数型に組み込まれたときはその互換性が発揮されないclass Animal {} class Cat: Animal {} let a: () -> Cat = { Cat () } let b: () -> Animal = a // OK let c: () -> [Cat] = { [Cat()] } let d: () -> [Animal] = c // NG: error: cannot convert value of type '() -> [Cat]' to specified type '() -> [Animal]' let e: [Cat] = [Cat()] e is [Animal] // truelet f: () -> Cat? = { Cat() } let g: () -> Animal? = f // OK こっちはできたwis の振る舞いってdo { let a: (Animal) -> () = { _ in } let b: (Cat) -> () = a // OK } do { let a: ([Animal]) -> () = { _ in } let b: ([Cat]) -> () = a // error: cannot convert value of type '([Animal]) -> ()' to specified type } do { let a: (Animal?) -> () = { _ in } let b: (Cat?) -> () = a // OK }do { let a: () -> () -> Cat = { { Cat() } } let b: () -> () -> Animal = a // OK } do { let a: () -> () -> [Cat] = { { [Cat()] } } let b: () -> () -> [Animal] = a // error: cannot convert value of type '() -> () -> [Cat]' to specified type '() -> () -> [Animal]' } do { let a: () -> () -> Cat? = { { Cat() } } let b: () -> () -> Animal? = a // OK }Array だけ壊れてるみたいdo { let a: [String: Cat] = ["": Cat()] let b: [String: Animal] = a // OK } do { let a: [String: [Cat]] = ["": [Cat()]] let b: [String: [Animal]] = a // OK } do { let a: [String: Cat?] = ["": Cat()] let b: [String: Animal?] = a // OK }as, is をoperatorにしてユーザーが定義できる様にして欲しくなる話だね。do { let a: () -> [String: Cat] = { ["": Cat()] } let b: () -> [String: Animal] = a // error: cannot convert value of type '() -> [String : Cat]' to specified type '() -> [String : Animal]' }代入文の型検査時は静的だから書き換えたisが評価できない
func as(lhs: A) -> Bの有無で決まるとか? (edited)func as<T, U>(_ v: My<T>) -> My<U> where U: T こういうの。無くても出来るかな?class Box<out T> とかやるのが楽だしわかりやすいとは思うけどstruct Box<T> { let value: T }T についての変性をどう扱うのかが気になってます。rintaro - 04/18/2017 この辺の話(Ty<Some> から Ty<Covariant> への変換) って https://devforums.apple.com/thread/261699 で語られているんですが、 https://devforums.apple.com/message/1102432#1102432 のArrayとかは出来るけど任意の value type には当てはまらないって文脈で > this doesn't apply to every value type (because value types can contain references and not enforce copy-on-write) らしいのですが、どういうケースなんでしょう?protocool Foo: classはできるけど、逆は出来ない。[Int] とかってboxingしないメモリレイアウトじゃないんか??unsafeBitCast([Int](), [String].self) これ動きそう 1> var a: [Int] = [0,1,2,3] a: [Int] = 4 values { [0] = 0 [1] = 1 [2] = 2 [3] = 3 } 2> var b: [String] = a as! [String] Could not cast value of type 'Swift.Int' (0x1013e0430) to 'Swift.String' (0x1013e36f8). 2017-10-06 11:35:18.511301+0900 repl_swift[28561:24355268] Could not cast value of type 'Swift.Int' (0x1013e0430) to 'Swift.String' (0x1013e36f8). b: [String] = <extracting data from value failed> Execution interrupted. Enter code to recover and continue. Enter LLDB commands to investigate (type :help for assistance.) Process 28561 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT frame #0: 0x00007fffc4cf5d42 libsystem_kernel.dylib`__pthread_kill + 10 libsystem_kernel.dylib`__pthread_kill: -> 0x7fffc4cf5d42 <+10>: jae 0x7fffc4cf5d4c ; <+20> 0x7fffc4cf5d44 <+12>: movq %rax, %rdi 0x7fffc4cf5d47 <+15>: jmp 0x7fffc4ceecaf ; cerror_nocancel 0x7fffc4cf5d4c <+20>: retq Target 0: (repl_swift) stopped.class Animal {} class Cat: Animal {} let a: [[Cat]] = [[Cat()]] a is [[Animal]] // truetrue になるんだっけis の検査と as の成功が一致してないといけないのか。is はあくまでインスタンスをチェックしてるだけで式の型を調べているわけではないと。 (edited) 1> let a: [Int] = [] a: [Int] = 0 values 2> type(of: a) $R0: [Int].Type = [Int] 1> let a: [Int] = [] a: [Int] = 0 values 2> type(of: a) $R0: [Int].Type = [Int] 3> func foo<T>(_ value: T) { 4. print(type(of: value)) 5. } 6> foo(a) Array<Int>let cats: [Cat] = [Cat()] let animals: [Animal] = cats 一瞬、↑みたいなケースで cats は animals にコピーされないからインスタンスの型という概念が成り立たないかと思ったけど、コピーされないのはあくまでバッファであって、インスタンス自体の領域は別だからやっぱりインスタンスの型で判断できる気がする。animals から cats に as で戻せなくなるのか。 (edited)Optional の is や as もややこしくなるな・・・。final class _EmptyArrayStorage : _ContiguousArrayStorageBase { } final class _ContiguousArrayStorage<Element> : _ContiguousArrayStorageBase { } // rawValue is passed inout to _isUnique. Although its value // is unchanged, it must appear mutable to the optimizer. @_versioned internal var rawValue: Builtin.BridgeObject @_inlineable // FIXME(sil-serialize-all) public // @testable var nativeInstance: Native { @inline(__always) get { _sanityCheck(isNative) return Builtin.castReferenceFromBridgeObject(rawValue) } } var objCInstance: ObjC { @inline(__always) get { _sanityCheck(isObjC) return Builtin.castReferenceFromBridgeObject(rawValue) } }internal typealias _ArrayBridgeStorage = _BridgeStorage<_ContiguousArrayStorageBase, _NSArrayCore> で、左側が さっきの pure swift のときの Storage の _ContiguousArrayStorageBase だから、as とかでObjC側にブリッジしたりするときにめんどくさいことが起こるんだろう・・・[Animal] as? [Cat] これを実現するためには、1要素毎にキャストして成功したらOKみたいな処理にする必要があってvar a: [Int] = [] var b: [String] = [] print(a == b) これで結論が出ると思ったらコンパイルエラーだったwfunc foo(x: Any) { switch x { case let strings as [String]: print("strings \(strings)") case let integers as [Int]: print("integers \(integers)") default: print("unknwon") } } let ints: [Int] = [] foo(x: ints) これで strings 判定されちゃうことになるので、やっぱキャスト出来ちゃうと駄目だと思います。 (edited)let x = [Int?.none, Int?.none, Int?.none] let y = x as? [String?] // [nil, nil, nil] これもなかなか。func foo<T>(value: T, type: T.Type) { switch type { case is Array<String>.Type: print("strings \(value as! Array<String>)") case is Array<Int>.Type: print("integers \(value as! Array<Int>)") default: print("unknwon") } } let ints: [Int] = [] foo(value: ints, type: type(of: ints)) // integers [] (edited)func cast<T, U>(from: U) -> T? { return from as? T } let z: Int?? = cast(from: String?.none) switch z { case .none: print("a") case .some(.none): print("b") // ココ case .some(.some): print("c") } で、やはりOptional.noneも同様にキャストが成功している。func foo<T>(value: T) { switch T.self { case is Array<String>.Type: print("strings \(value as! Array<String>)") case is Array<Int>.Type: print("integers \(value as! Array<Int>)") default: print("unknwon") } } let ints: [Int] = [] foo(value: ints)var a: [Int] = [] var b: [String]? = a as? [String] // Cast from '[Int]' to unrelated type '[String]' always fails この警告だけ (edited)func foo(_ x: Any) { switch type(of: x) { case is [String].Type: print("strings \(x as! [String])") case is [Int].Type: print("integers \(x as! [Int])") default: print("unknwon") } } var a: [Int] = [] foo(a) // integers [] (edited)as で出来るのはおかしい。こっちは世界の崩壊を招くlet x = [Int?.none, Int?.none, Int?.none] x is [String?] // true type(of: x) is [String?].Type // falselet x = [Int??.none, Int??.none, Int??.none] let y = x as? [Int?] // [nil, nil, nil]let x = [Int?.none, Int?.none, Int?.none] type(of: x) == [Int?].self // true== でも書けるね、継承を区別したいときはこれが必要になる (edited)as? の考え方が違うってだけだと思う/// If the dynamic type of `obj` doesn't implement a `getIntegerValue()` /// method, the system returns a runtime error when you initialize /// `certainValue`. /// /// Alternatively, if you need to test whether `obj.getIntegerValue()` exists, /// use optional binding before calling the method. /// /// if let f = obj.getIntegerValue { /// print("The value of 'obj' is \(f())") /// } else { /// print("'obj' does not have a 'getIntegerValue()' method") /// } /// // Prints "The value of 'obj' is 100" public typealias AnyObject (edited)public typealias AnyObject = Builtin.AnyObject public typealias AnyClass = AnyObject.Typetypealias Any = protocol<> が実際にあったけど、 & 記法になったタイミングで Any がキーワードになり、Parser で特別扱いになりました。typealias Any = Builtin.Any になるかもしれないですね。let x = Int??.none as? Int? let y = Int?.none as? Int?? switch x { case .none: print("print") case .some: print("-") } switch y { case .none: print("-") case .some: print("print") } (edited)let x = [Int?.none] as? [Int??] switch x?.first! { case .none: print("x") // !! case .some(.none): print("y") case .some(.some): print("z") } let y = [Int?.none] as [Int??] switch y.first! { case .none: print("x") case .some(.none): print("y") // !! case .some(.some): print("z") } (edited)as? の使用に対して警告だしながら > Conditional cast from '[Optional<Int>]' to '[Int??]' always succeedsEncoder, Decoderはサポートする型により、Codable自身のコーディング表現をオーバーライドする様になってます。URLをURL自身にエンコードさせると、["relative":"http://apple.com"]みたいな辞書表現になります。On implementing Codable conformance, if it uses container.encode(_:…) instead of container.encodeIfPresent(_:…), container.encode(url, forKey: .url) produces dictionary. But, `JSONDecoder` tries to decode Optional<URL> from string.JSONEncoderは"http://apple.com"として扱いたいので、EncodableがURLだったらこうする、と特別扱いします。 } else if T.self == URL.self { // Encode URLs as single strings. return self.box((value as! URL).absoluteString)Optional, Set, Arrayにこれらの型が入っていた場合にも特別扱いしなければいけないのですが、Conditional Conformanceがないため、Decodableのコードが素直にかけませんでした。extension Optional : Decodable
extension Optional : Decodable /* where Wrapped : Decodable */ { @_inlineable // FIXME(sil-serialize-all) public init(from decoder: Decoder) throws { // Initialize self here so we can get type(of: self). self = .none assertTypeIsDecodable(Wrapped.self, in: type(of: self)) let container = try decoder.singleValueContainer() if !container.decodeNil() { let metaType = (Wrapped.self as! Decodable.Type) let element = try metaType.init(from: container) self = .some(element as! Wrapped) } } } (Swift 4.0のコードに変更) (edited)Decoderの処理を優先して、こう書きたい。 let container = try decoder.singleValueContainer() if !container.decodeNil() { - let metaType = (Wrapped.self as! Decodable.Type) - let element = try metaType.init(__from: container) - self = .some(element as! Wrapped) + self = .some(try container.decode(Wrapped.self)) } } } (edited)JSONEncoderとかのCoderで特別扱いされるURLの側で「JSONEncoderだったら辞書表現をやめる」みたいな力技で対応されていました。 https://github.com/apple/swift/pull/10766 (edited)Coderではバグっていたのです。Coder実装がどれくらいあるのか知りませんが…YAMLのCoder実装は未リリース。これがSwift 4.0.1で直りそう、というお話。 4.0.1に入るかどうか微妙な時期らしい…let results = scheduler.createObserver(Void.self) XCTAssertEqual(results.events, [next(0, ()), next(10, ())]) と書くとコンパイルエラーになってしまいます。 どのように書くと良いのでしょうか…? (そもそもObservable<Void>はTestableObserverでテストを書くべきでは無いのでしょうか…?)() == () // truefunc f<X: Equatable>(x: X) {} f(x: ())public func == (lhs: Recorded<Void>, rhs: Recorded<Void>) -> Bool { return lhs.time == rhs.time } これその辺に書いたら何とかなりませんかねimport Foundation class A: Encodable { let f1: String init(f1: String) { self.f1 = f1 } } class B: A { let f2: String init(f1: String, f2: String) { self.f2 = f2 super.init(f1: f1) } } let b = B(f1: "f1", f2: "f2") let x = try JSONEncoder().encode(b) String(data: x, encoding: .utf8) // -> { "f1": "f1" } (edited)protocol C: Encodable { var f3: String { get } } struct D: C { var f3: String } let d: C = D(f3: "f3") let y = try JSONEncoder().encode(d) // ここでコンパイルエラー (edited)Dとして渡すと通りますCにencodeメソッドを足してDや各々でエンコードってのが良さそうですかね/// A type that can encode itself to an external representation. public protocol Encodable { /// Encodes this value into the given encoder. /// /// If the value fails to encode anything, `encoder` will encode an empty /// keyed container in its place. /// /// This function throws an error if any values are invalid for the given /// encoder's format. /// /// - Parameter encoder: The encoder to write data to. func encode(to encoder: Encoder) throws }protocol C: Encodable { var f3: String { get } func encode() throws -> Data } struct D: C { var f3: String func encode() throws -> Data { return try JSONEncoder().encode(self) } } let d: C = D(f3: "f3") let y = try d.encode() open func encode<T : Encodable>(_ value: T) throws -> Data {protocol P {} class Cat : P {} func f<X: P>(_ p: X) {} var a: P = Cat() var b: Cat = Cat() f(a) // NG f(b) // OKC のextensionにencode書けばいいのか。protocol C: Encodable { var f3: String { get } func encode() throws -> Data } extension C { func encode() throws -> Data { return try JSONEncoder().encode(self) } } struct D: C { var f3: String } let d: C = D(f3: "f3") let y = try d.encode() これがいける?toJSON() -> Data という名前の方が良さそうOptional.noneは常にフィールド自体省略になってnullにすることができない気が…… fileprivate func box<T : Encodable>(_ value: T) throws -> NSObject { return try self.box_(value) ?? NSDictionary() } Container上では return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions)encode(to:)だとそうなりますね。OptionalだとencodeIfPresent(_:forKey:)が使われるコードが生成される。これをencode(_:forKey:)を使うコードを自分で書けばnullが使われる。 https://github.com/apple/swift/blob/master/lib/Sema/DerivedConformanceCodable.cpp#L623-L624 (edited)Codable使う旨味が減っちゃいますねぇ。 Optional.noneのあつかいもstrategyで書けたらいいんじゃないかと思うんですが データ全体じゃなく部分ごとにここは省略したいとかある可能性もあるんで微妙ですかねstruct Foo: Encodable { var optionalInt: Int? private struct FooEncode: Encodable { var optionalInt: KeyedOptional<Int> /* Foo <-> FooEncode 変換書く */ } func encode() throws -> Data { return FooEncode(self).encode() } } こげな{ "objects": [ { "key": 1, "value": "foo" }, { "key": "2", "value": "bar" } ] } こういう意地悪すると、 enum Key: Codable { case int(Int) case string(String) } こんなのが生える{ "objects": [ { "key": 1, "value": "foo" }, { "key": null, "value": "bar" } ] } で、 struct Object: Codable { let key: Int? let value: String } (edited)superEncoder/superDecoderの利用は必須ではないですね。superclassのデータをキーsuperに入れるかどうかはユーザー次第になってるかと。public class Comprehensive: Encodable { private let field1: String private let field2: String? private let field3: String? fileprivate init(field1: String, field2: String?, field3: String?) { self.field1 = field1 self.field2 = field2 self.field3 = field3 } } public class Child1: Comprehensive { public init(field1: String, field2: String) { super.init(field1: field1, field2: field2, field3: nil) } } public class Child2: Comprehensive { public init(field1: String, field3: String) { super.init(field1: field1, field2: nil, field3: field3) } } めちゃくちゃ汚いですがこれでやりたかったことはできそうですpublic class AnyKeyPath {} public class PartialKeyPath<Root> : AnyKeyPath {} public class KeyPath<Root, Value> : PartialKeyPath<Root> {} public class WritableKeyPath<Root, Value> : KeyPath<Root, Value> {} public class ReferenceWritableKeyPath<Root, Value> : WritableKeyPath<Root, Value> {} (edited)responds(to:)とか使えるという話import Foundation class Cat { @objc func eat() {} } (Cat() as AnyObject).responds(to: #selector(Cat.eat)) // true#selector(Cat.eat) ってやってるけどclass Dog { @objc func eat() {} }@objcがついてないと当然Selectorは生えないのでfalseになる- (oneway void)release;Codableに準拠した型のletプロパティは、エンコードはされるけどデコードされることはないのね。letプロパティをエンコードされない様にするには、letプロパティを含まないCodingKeysを用意すると。letプロパティだ。NSDictionaryからDataを経由せずに直接デコードしたい… 作った。 https://github.com/norio-nomura/ObjectEncoder[String: Any], [Any] or Any as payload.import Foundation import ObjectEncoder // single value let string = "Hello, ObjectEncoder" let encodedString = try ObjectEncoder().encode(string) (encodedString as AnyObject).isEqual(to: string) // true let decodedString = try ObjectDecoder().decode(String.self, from: encodedString) // dictionary struct S: Codable { let p1: String } let s = S(p1: "string") guard let encodedS = try ObjectEncoder().encode(s) as? [String: Any] else { fatalError() } encodedS["p1"] // "string" let decodedS = try ObjectDecoder().decode(S.self, from: encodedS) decodedS.p1 // "string" // array let array: [S] = [s, s] guard let encoded = try ObjectEncoder().encode(array) as? [[String: Any]] else { fatalError() } encoded[0]["p1"] // "string" let decoded = try ObjectDecoder().decode([S].self, from: encoded) decoded[0].p1 // "string"JSONEncoderやPropertyListEncoderみたいに、各型についての特殊な処理が一切入っていないCoders実装になってます。[String: Anyや[Any]へそのまま突っ込むイメージ。let source = CGImageSourceCreateWithURL(url as CFURL, nil), let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as NSDictionary? let decoded: ImageProperties = try ObjectDecoder().decode(from: properties) みたいなAPIからNSDictionaryで返ってきたデータのモデリング。 (edited)JSONEncoderかPropertyListEncoderへData以外も受け付けるAPIを作ってPR出すことも考えたけど、変換部分が邪魔だった。{ “id”: 1, “url”: “” } というAPIのレスポンスがあったとして、 struct Hoge: Codable { let id: Int let url: URL? } これでデコードしようとすると、Invalid URL StringということでdataCorrupted扱いになります。このとき、urlをnilで取り扱うためのワークアラウンド、どなたかお持ちですか? (edited)struct Hoge: Codable { let id: Int let jsonUrl: String? var url: URL { get { ... } set { ... } } }struct Landmark: Codable { var name: String var foundingYear: Int var location: Coordinate var vantagePoints: [Coordinate] enum CodingKeys: String, CodingKey { case name = "title" case foundingYear = "founding_date" case location case vantagePoints } }struct Hoge: Codable { let id: Int private let jsonUrl: String? var url: URL? { return URL(string: jsonUrl) } enum CodingKeys: String, CodingKey { case id case jsonUrl = "url" } } (edited)struct Hoge: Codable { let id: Int private let jsonUrl: String? var url: URL? { return jsonUrl.flatMap(URL.init) } enum CodingKeys: String, CodingKey { case id case jsonUrl = "url" } } (edited)struct Hoge: Codable { let id: Int let url: String? } extension Hoge { var urlurl: URL? {return url.flatMap {URL(string: $0)}} } Codableがjsonをそのままうつしていると考えればこう (edited)url: String は image: UIImageView と同じレベルでやめたいurl のほうを使わずに urlurl を使うのが注意点かurl: String は合理的だと思いますよ。 URL オブジェクトはデータ構造というよりはユーティリティ。struct _Hoge: Codable { 略 } と struct Hoge { 略 } でこいつら相互変換可能に。hoge.カッコイイ名前.url になるのではHoge と Hoge.JSON にするの良さそうCGSize(width: 100, height: 50) に対応するのは { "width": 100, "height": 50 } ではなくて [100, 50] なんですよid: Int から id String に変わったとかあるので、外部APIを最初に受けるところはあまり厳格にしてもというのはあります。import Foundation struct Model<Base: Modelable> { var base: Base } protocol Modelable { var model: Model<Self> { get } } extension Modelable { var model: Model<Self> { return Model(base: self) } } struct Hoge: Codable, Modelable { fileprivate var int: Int fileprivate var url: String? } extension Model where Base == Hoge { var int: Int { return base.int } var url: URL? { return base.url.flatMap(URL.init) } } func x(hoge: Hoge) { hoge.model.url // URL? } よさそうstruct Cat { subscript(ex ex: Void) -> Ex<Cat> { get { return Ex(self) } set { self = newValue.t } } var age: Int = 3 } struct Ex<T> { init(_ t: T) { self.t = t } var t: T } var cat = Cat() cat[ex: ()].t.age = 6 print(cat) // Cat(age: 6)func M<A: Modelable>(_ arg: A) -> Never { fatalError() } こいつ作っておけば cat[M].t.age = 6ってできるんじゃん?cat[Rx].tap.subscribe ...import Foundation struct Model<Base: Modelable> { var base: Base } protocol Modelable { var model: Model<Self> { get } } extension Modelable { var model: Model<Self> { return Model(base: self) } } struct Hoge: Codable, Modelable { fileprivate var int: Int fileprivate var url: String? var model: Model<Hoge> { get { return Model(base: self) } set { self = newValue.base } } } extension Model where Base == Hoge { var int: Int { get { return base.int } set { base.int = newValue } } var url: URL? { get { return base.url.flatMap(URL.init) } set { base.url = newValue?.absoluteString } } } func x(hoge: Hoge) { hoge.model.int var hoge = hoge hoge.model.url = URL(string: "https://www.google.com") } いけた。struct Model<Base: Modelable> { var base: Base } protocol Modelable { var model: Model<Self> { get } } extension Modelable { var model: Model<Self> { get { return Model(base: self) } set { self = newValue.base } } } struct Hoge: Codable, Modelable { fileprivate var int: Int fileprivate var url: String? } extension Model where Base == Hoge { var int: Int { get { return base.int } set { base.int = newValue } } var url: URL? { get { return base.url.flatMap(URL.init) } set { base.url = newValue?.absoluteString } } } func x(hoge: Hoge) { hoge.model.int var hoge = hoge hoge.model.url = URL(string: "https://www.google.com") }Modelに対してJSON<Model>を作りたい (edited)URLComponents として保持するのもありなのかも?と思ったけど、これはどういう状態なんだろうか。 import Foundation var comps = URLComponents(string: "")! comps.url! // (no URL) (edited).url が作れないって感じ?String で持つべきだと思います?UUID にするのはもちろんやるべきですし、私もそう書きます。UUID で定義するなら、 UUID を String で持つべきなのはどういうケースですか? (edited)UUID、サーバから受ける場合は String?url.flatMap {URL(string: $0)} を都度書きたくない/// デコードしたStringと、TypedなURLは纏めて独自型に押し込めるほうがいい struct RobustURL: Codable { let rawValue: String var typed: URL? { return URL(string: rawValue) } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(rawValue) } init(from decoder: Decoder) throws { rawValue = try decoder.singleValueContainer().decode(String.self) } } /// CoreGraphicに依存しない独自型 struct Size: Codable { let width: Double let height: Double } import CoreGraphics extension Size { var asCGSize: CGSize { return CGSize(width: width, height: height) } } struct Fuga: Codable { let url1: RobustURL // 不正なURLが来うることを型として示せる let url2: RobustURL // 不正なURLが来うることを型として示せる let url3: URL // 不正なURLが来ないことを型として示せる let size: Size } let data = """ { "url1": "https://failable.url", "url2": "不正なURL", "url3": "https://ensured.url", "size": { "width": 100, "height": 50 } } """.data(using: .utf8)! let fuga = try! JSONDecoder().decode(Fuga.self, from: data) fuga.url1.typed // https://failable.url fuga.url2.typed // nil fuga.url3 // https://ensured.url fuga.size.asCGSize // {w 100 h 50} let json = try! JSONEncoder().encode(fuga) print(String(data: json, encoding: .utf8)!) // {"size":{"width":100,"height":50},"url3":"https:\/\/ensured.url","url1":"https:\/\/failable.url","url2":"不正なURL"}ウソもデコードできるURL型を自分で定義これすねasURL は欲しくないですねstruct Bar: Codable, RawRepresentable { let rawValue: String init?(rawValue: String) { self.rawValue = rawValue } } これはエンコードできないな。struct RobustURL: Codable, RawRepresentable { let rawValue: String var typed: URL? { return URL(string: rawValue) } init?(rawValue: String) { self.rawValue = rawValue } } すっきり{"rawValue": ...} の階層つくらないんですね、まじか‥init?(rawValue: String) も要らないんじゃないかな。publicにする時には要るか。internalなら要らないですね。: class をつけることが多いんだけどprotocol と mutating 関連の Proposal なかったっけ?: class にしてしまっているprotocol Foo: class {} class Bar<X: class> {} Bar<Foo>() // 🙅:class ってそこでもつかえる? :AnyObject にしてた。@objc protocol Foo: class {} class Bar<X: AnyObject> {} Bar<Foo>() // 🙆@objc は深入りしたくない・・・互換性のためにいろいろ諦めてそうだしちゃんと考えて無さそう (edited)@optional なメソッドとかSwift上にそんな概念ないのにちゃんと考慮されたりするし:class つけなくてもその前提条件だとコンパイルはできるんだけど、 考慮せずぜんぶ func にしてるんだって自覚を表明するためにつけといたほうがマシかなって思ってるAnyObject って参照型なら OK じゃなくてクラスじゃないとダメなのか。 1> let a: () -> Int = { 42 } 2. let b: AnyObject = a error: repl.swift:2:20: error: value of type '() -> Int' does not conform to specified type 'AnyObject' let b: AnyObject = a ^ as AnyObjecttypealias AnyObject 1> let a: () -> Int = { 42 } a: () -> Int = 0x00000001000c5030 $__lldb_expr8`closure #1 () -> Swift.Int in __lldb_expr_7 at repl.swift:1 2> let b = a b: () -> Int = 0x00000001000c5030 $__lldb_expr8`closure #1 () -> Swift.Int in __lldb_expr_7 at repl.swift:1 3> a === b error: repl.swift:3:3: error: cannot check reference equality of functions; operands here have types '() -> Int' and '() -> Int' a === b ~ ^ ~var f: () -> String = { "hello" } var array: [() -> String] = [] array.append(f) print(array[0] === f) /Users/omochi/temp/swer/Sources/swer/main.swift:5:16: Cannot check reference equality of functions; operands here have types '() -> String' and '() -> String'=== のオペランドの型が AnyObject なのかも。let c = { (i: Int) in print("\(i)") } func f(i: Int) { print("\(i)") } (c as AnyObject) === (c as AnyObject) // false (f as AnyObject) === (f as AnyObject) // false まあそもそもBoxのポインタ比較だと思うんでvar a = 0 let b: () -> Int = { a = a + 1; return a } let c = b print(b()) // 1 print(c()) // 2@convention()によって挙動がいろいろですね。c() は 1 にならないとおかしい。
@conventionは7種類あるらしい - ``@convention(thin)`` indicates a "thin" function reference, which uses the Swift calling convention with no special "self" or "context" parameters. - ``@convention(thick)`` indicates a "thick" function reference, which uses the Swift calling convention and carries a reference-counted context object used to represent captures or other state required by the function. - ``@convention(block)`` indicates an Objective-C compatible block reference. The function value is represented as a reference to the block object, which is an ``id``-compatible Objective-C object that embeds its invocation function within the object. The invocation function uses the C calling convention. - ``@convention(c)`` indicates a C function reference. The function value carries no context and uses the C calling convention. - ``@convention(objc_method)`` indicates an Objective-C method implementation. The function uses the C calling convention, with the SIL-level ``self`` parameter (by SIL convention mapped to the final formal parameter) mapped to the ``self`` and ``_cmd`` arguments of the implementation. - ``@convention(method)`` indicates a Swift instance method implementation. The function uses the Swift calling convention, using the special ``self`` parameter. - ``@convention(witness_method)`` indicates a Swift protocol method implementation. The function's polymorphic convention is emitted in such a way as to guarantee that it is polymorphic across all possible implementors of the protocol.@convention(thin) indicates a "thin" function reference, which uses the Swift calling convention with no special "self" or "context" parameters. あー、ここに出てくる、 self と context がミソっぽいなあ 今の話let b: () -> Int = { [x=a] in a = x + 1; return a }AnyObjectには入らなかった>7種類。class やめるのダメ?var a = 3 let b: () -> Int = { [x=a] in a = x + 1 return a } print(b()) // 4 print(b()) // 4 print(b()) // 4idにはブロックが入った気がするから、AnyObject != idvar a = 3 let b: () -> Int = { [x=a] in a = x + 1 return a } let c = b print(b()) print(c()) print(b()) print(c()) 4 4 4 4class 使うとか? class Ref<Value> { var value: Value }& と * がほしくなってきた。 struct と & と * の世界、実は良かったのでは? malloc や free が面倒だっただけで、そこをリファレンスカウントで勝手にやってくれるなら。& じゃなくて、実体コピーした上で参照型化かな。struct Foo を作ったけど、それを共有したいときは Foo * として取り扱えるとうれしい。 (edited)* の意味は C と同じになるけど、 & はちょっと違うから別の記号が良さそう。Box<Foo> と同じなんだけどbox 1Optional や Array みたいに Box ( Reference でもいい)のシュガーがあればいいのかも。Foo* が Reference<Foo>weak var foo: Foo*? こうなる? (edited)class Reference<Value> { var value: Value }prefix func * もほしいかFoo* が Reference<Foo> だとうれしくない?weak var a: Reference<Int>? で良いんじゃない? (edited)weak var foo: Foo*? かな?let a: () -> Int = { 42 } let block = a as @convention(block) () -> Int as AnyObjectlet a: () -> Int = { 42 } let block = a as AnyObject print(block)
as AnyObject はそもそもそのままできそうですよlet a: () -> Int = { 42 } print(a as AnyObject === a as AnyObject) // false let b = a as @convention(block) () -> Int print(b as AnyObject === b as AnyObject) // true() -> Int false @convention(block) () -> Int truea は as AnyObject でボクシングされるけどb はすでに AnyObject だからlet a: () -> Int = { 42 } let b = a as AnyObject b === b // truelet a: () -> Int = { 42 } print(type(of: a)) print(a as AnyObject === a as AnyObject) let b: @convention(block) () -> Int = a as @convention(block) () -> Int print(type(of: b)) print(b as AnyObject === b as AnyObject) var x: AnyObject = b // NG: value of type '@convention(block) () -> Int' does not conform to specified type 'AnyObject'dump(P.method)もダメだった。var x: AnyObject = b // NG: value of type '@convention(block) () -> Int' does not conform to specified type 'AnyObject' (edited)poをやるみたいなの。>dump (edited)print(b as AnyObject === b as AnyObject) // true var x: AnyObject = b // NG: value of type '@convention(block) () -> Int' does not conform to specified type 'AnyObject'import Foundation let a = "str"; let b: NSString = aclass Cat {} let a: AnyObject = Cat() print(type(of: a)) // Cat
func f() -> Int { return 42 } let a: AnyObject = f as AnyObject print(type(of: a)) // _SwiftValue_SwiftValue なる型になってた。swift-string.swift:3:19: error: cannot convert value of type 'String' to specified type 'NSString' let b: NSString = a ^ as NSString; が付いてるas NSString なら通る。明示的なキャストを言語的に強制してるということです。:tarunon: 絵文字ほしくなってきたなfalse だから。 import Foundation let a = "str" a as NSString === a as NSString // false=== が true になるところimport Foundation let str = "str" let b = str as NSString (b as AnyObject === b as AnyObject) // true と同じじゃないかと。 (edited)let b: @convention(block) () -> Int = { 42 } b as AnyObject === b as AnyObject // trueimport Foundation let str = "str" let b = str as NSString let c: AnyObject = b // OKlet b: @convention(block) () -> Int = { 42 } b as AnyObject === b as AnyObject // true let c: AnyObject = b // ERRORlet b: @convention(block) () -> Int = { 42 } b as AnyObject === b as AnyObject // true b is AnyObject // true let c: AnyObject = b // ERRORis AnyObject が true なのに代入できない・・・import Foundation let a = "aaa" a is NSString let b: NSString = a is trueだけど代入できないのはよくあるからOptional も含め@convention(block) の時点で ObjC の世界に入っているわけだから、 AnyObject にそのまま突っ込めてもいい気はしますね。protocol Proto : AnyObject {} class Box1<X> {} Box1<Proto>() // OK class Box2<X: AnyObject> {} Box2<Proto>() // NG: 'Proto' is not convertible to 'AnyObject''Proto' is not convertible to 'AnyObject' ってエラーメッセージ出てるけど Proto : AnyObject なんだけどなあprotocol Proto {} struct S: Proto {} func foo<P: Proto>(x: P) {} let p: Proto = S() foo(x: p) // error: cannot invoke 'foo' with an argument list of type '(x: Proto)' これと同じですね。AnySequence とか使いたくなるケースの 8 割くらいは実はいらないんじゃないかなぁ。as とか is の挙動がReference と同じように。後は使いやすいシュガーがあれば。protocol と参照型の相性が良くないような。{ ∃X, X: Animal } という存在型を略して Animal 型と書くのをやめてぱっと見で存在型だってわかるようにすればもうちょい理解しやすくなりそうですねtypealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element> let strings: AnySequence<String> = ["a", "b", "c"]P1 & P2 は Any<T where T: P1, T: P2> とか?Any<P1 & P2> が Exitentialになるという仕様になってほしい P1 & P2 はあくまで protocol (edited)protocol<P1, P2> が P1 & P2 になりました。import UIKit class MyScrollViewDelegate: NSObject, UIScrollViewDelegate { } let myScrollViewDelegate = MyScrollViewDelegate() let tableView = UITableView() let scrollView = tableView as UIScrollView scrollView.delegate = myScrollViewDelegate tableView.delegate is UITableViewDelegate // true myScrollViewDelegate === tableView.delegate // true myScrollViewDelegate is UITableViewDelegate // false Objc絡むととたんにやばくなるやつunrecognized selector sent to instance 0xXXXXX いつものだwAnimal がbark() メソッドをもってるとして、Any<Animal> 型のanimal に対して animal.bark() を呼びだせるのはAny<Animal>: Animal なわけではなく、 Opening existentials のシンタックスシュガーだって考えればよさそう? let animal: Any<Animal> = Dog() // OK animal.bark() // ↑は以下のシンタックスシュガー // aの型はA。AはAnimalに準拠。 let a = animal openas A a.bark()struct S { var a: Int = 3, b: Int = 4 }CGFloat って Swift 3.1 まで Float や Double の typealias じゃなかったっけ? https://developer.apple.com/documentation/coregraphics/cgfloatstruct になった?struct でしたっけ?struct の方が良いと思います。(環境によって Float や Double に切り替わるとやっかいなので)CGFloat は昔 Double か Float の typealias だったような気が…Obj-Cとの記憶が混乱したのか TimeInterval と ObjC の両方と混同してたような気がしますCGFloat のコンパイルが通らなくなる夢を見てた気がしてたんですが、この頃の記憶かも。CoreGraphics だし import Foundation しないと使えないし@_fixed_layout public struct CGFloat { #if arch(i386) || arch(arm) /// The native type used to store the CGFloat, which is Float on /// 32-bit architectures and Double on 64-bit architectures. public typealias NativeType = Float #elseif arch(x86_64) || arch(arm64) /// The native type used to store the CGFloat, which is Float on /// 32-bit architectures and Double on 64-bit architectures. public typealias NativeType = Double #endif ... /// The native value. public var native: NativeType } (edited)NativeType の typealias になったね +public struct CGFloat { +#if arch(i386) || arch(arm) + public typealias UnderlyingType = Float +#elseif arch(x86_64) || arch(arm64) + public typealias UnderlyingType = Double +#endif (edited)昨日話していてそんな気がしたんですが やっぱりOptionalの暗黙変換とOptionalの共変性は矛盾した結果を生むので両方入ってるのは厳しいですね// Kotlin open class Animal class Cat: Animal() // Rule A // Cat is subtype of Animal run { val cat: Cat = Cat() val animal: Animal = cat } // Rule B // Int is subtype of Int? run { val int: Int = 0 val intOptional: Int? = int // 0 } // Rule C // Optional has covariance run { val catOptional: Cat? = null val animalOptional: Animal? = catOptional // null } // Question // What happen replace `Cat : Animal` to `Int : Int?` run { val intOptional: Int? = null val intOptionalOptional: Int?? = intOptional // null (Int?? == Int?) }String?.none as? Int? => Int??.some(.none) は確かにおかしい結果を生むんですが、それ以前に共変性周りが崩壊していることに気がついたInt?? でネストを表現できないですがInt は Int? のサブタイプじゃないですからねぇ。Optional<Cat> が Optional<Animal> のサブタイプでも、 Int が Int? のサブタイプでなければ Optional<Int> は Optional<Int?> のサブタイプにならないですし。String?.none as? Int? => Int??.some(.none) こいつをウソのサブタイプを倒した後でどう説明するのが良いのか、どう言う振る舞いであるべきなのかを考えないといけない気がするCat?.none as Animal? これが暗黙のmapであるとするのであれば上の結果は正しいということになりますねString?.none as? Int? => Int??.some(.none)
String → Int はパースですか?
String?.none as? Int? ってやると結果が Int??.some(.none)になるんですよ。つまりキャストが成功するas? の ? を忘れてた。 (edited)[String]() as? [Int] これは 空配列になる。 1> Int?.none as? Int? $R0: Int?? = nil (edited) 2> print(Int?.none as? Int?) Optional(nil) 1> dump(Int?.none as? Int?) ▿ Optional(nil) - some: nil $R0: Int?? = nilString?.none as? Int? // .some(.none).none であるべきだと思います。[String]() as? [Int] // .some([]) (edited).none であるべきかとAnimal?.none as? Cat? ではこれは?([String]() as [Any]) as? [Int] (edited)Animal?.none as? Cat? ダメな気がしてきたlet cats = [Cat]() let animals: [Animal] = cats // OK let cats2: [Cat] = animals as! [Cat] // これは? (edited)let animals: [Animal] = cats // OK ↑ここで CoW を効かせたいからなぁ。map すると先行コピーされちゃう。 (edited)map でもいい気も?List は参照型だけど、値型+ジェネリクス+ out のときどうなるのかなって。interface しか out 付けれなかった。 using System; namespace HelloWorld { interface IBox<out T> { T Get(); } struct SBox<T> : IBox<T> { private T _value; public SBox(T t) { _value = t; } public T Get() { return _value; } } class CBox<T> : IBox<T> { private T _value; public CBox(T t) { _value = t; } public T Get() { return _value; } } class Animal {} class Cat : Animal {} class Hello { static void Main() { IBox<Cat> sCat = new SBox<Cat>(new Cat()); IBox<Animal> sAnimal = sCat; // OK sCat = (IBox<Cat>)sAnimal; // OK Console.WriteLine(sCat); IBox<Cat> cCat = new CBox<Cat>(new Cat()); IBox<Animal> cAnimal = cCat; // OK cCat = (IBox<Cat>)cAnimal; // OK Console.WriteLine(cCat); } } }beginAsync の throws をなくそうという話への反応が少なくて悲しい。 Chris Lattner と Joe Groff の見解が知りたい。(Foo) async -> Void の戻り値を受けなくても別に問題ないですよね?await foo() await bar() await baz()foo の非同期処理が完了後 bar が実行され、 bar が完了後 baz が実行されるだけで。 (edited)async -> Void ができなきゃいけないって意味ですね。beginAsync の throws について反応がないのはなんでなんだろう?静的チェックの安全性を壊すか壊さないかの重要な話だと思うんですけどねぇ・・・。class Cat { func nya(_ a: Int) -> String { return String(a) } } let nya: (Cat) -> (Int) -> String = Cat.nya let cat = Cat() nya(cat)(3) // "3" Cat.nya(cat)(4) // "4"- Indices.swift.gyb + Indices.swiftCountableRange 系の削除に期待。CountableRange 消す PR 書くの気持ち良さそう。CodableとかAPI自体を見直さないとダメそう。>Conditional Conformanceimport Foundation struct S<Wrapped> { var value: Wrapped } extension S: Codable where Wrapped: Codable { enum CodingKeys: String, CodingKey { case value } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) value = try container.decode(Wrapped.self, forKey: .value) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(value, forKey: .value) } } let s = S<Int>(value: 1) let encoder = JSONEncoder() let data = try encoder.encode(s) print(String(data: data, encoding: .utf8)!) // {"value":1} がswift-DEVELOPMENT-SNAPSHOT-2017-11-13-aで動いた。extension S: Sequence where Wrapped: Sequence { func makeIterator() -> Wrapped.Iterator { return value.makeIterator() } } はコンパイラクラッシュSequence は Element, Iterator, SubSequence とあって、それらが関係しているので複雑そうです。Wrapped.Iterator を自分の Iterator として解決する辺りとかヤバそう。[Any]はCodableでは無くなる?extension Array: Codable where Element: Codable {} かと。Equatable とかと同様に。[AnyHashable: Any]も。[String: Any]の代わりに[String: Codable]を使っても、Encoder,Decoder側に扱うAPIが無いかも。 Fatal error: Dictionary<String, Decodable & Encodable> does not conform to Encodable because Encodable does not conform to itself. You must use a concrete type to encode or decode.extension Dictionary: Codable where Value: Codable {} が必要な気がします。正確には Encodable と Decodable 別々に。extension Dictionary: Codable where Key == String, Value: Codable {} こうかな?AnyCodableみたいなものが必要なんじゃないかな?と。Codable は associatedtype も Self も持たないだたの protocol だから、 Codable 型として existential になりますよね? struct Foo: Codable {} let foo: Codable = Foo() // OKprotocol P0 { } struct A : P0 {} func f<X: P0>(_ x: X) {} var a: P0 = A() f(a) // error<T: Encodable>を受けるAPIはありますがEncodableを直接受けるAPIは無いです。<T: Encodable>ではなくEncodableを直接受け取れば良いのでは?」とコメントしたのですが、受け入れられませんでした。AnySequence がなくせる話と絡んで、 Any<Codable> とかできるようになるのかな? 1> let a: Any = 42 a: Int = 42 2> func foo<T: Any>(_ x: T) { print(x) } 3> foo(a) 42Encodableではなく<T: Encodable>なのは、decode()側が<T: Decodable>を使う必要があり、そちらと一貫性を持たせる為だと言われた。よさそうですね! んで、 `Animal` が`bark()` メソッドをもってるとして、`Any<Animal>` 型の`animal` に対して `animal.bark()` を呼びだせるのは`Any<Animal>: Animal` なわけではなく、 Opening existentials のシンタックスシュガーだって考えればよさそう? let animal: Any<Animal> = Dog() // OK animal.bark() // ↑は以下のシンタックスシュガー // aの型はA。AはAnimalに準拠。 let a = animal openas A a.bark()
@ukitaka 前結局理解できなかったのだけどAny<Animal>がAnimalのextentialでなくなる理由とか嬉しい点って何ですか?func foo<T: SomeProtocol>(...) これに対して、Tの型はかなりカッチリしていてlet a = animal openas A // 将来opening existentialが入るとするa の型ってなんですか?test2 の型パラメータの <A> と同じって意味?protocol Animal {} struct Cat: Animal {} func bar<T: Animal>(_ x: T) { print(x) } let b: Animal = Cat() bar(b) // NG (edited)func foo<T: Sequence>(_ x: T) { print(x) } let a: AnySequence<Int> = AnySequence([2, 3, 5]) foo(a) // OK (edited)typealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element>
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials (edited)foo に Any<Sequence where .Iterator.Element == Element> という Existential Type を渡せるということになります。Animal が bar に渡せてもいいように思うのですがいかがでしょう?remove()実装、1個づつmoveInitialize()してるけど、まとめて出来るはず。 https://qiita.com/omochimetaru/items/f32d81eaa4e9750293cdremoveSubrange も O(n) だからいいじゃないかなと思ったりする https://developer.apple.com/documentation/swift/array/2884498-removesubrangemoveInitialize にしても O(n) なのは変わらないですね でも、ループを自分でやるより関数に畳み込むほうが最適化も期待できるし、コードはシンプルにできるので、ちゃんと動くなら書き換えたほうが良さそう。remove 系のメソッド全部確認して見たらやはりremoveLast だけ O(1) だった /// - source: A pointer to the values to copy. The memory region /// `source..<(source + count)` must be initialized. The memory regions /// referenced by `source` and this pointer may overlap.source and this may overlap だからExpressibleByRegularExpressionLiteral を話してる中で Chris Lattner が↓言ってるんですがおもしろくないですか? 2) I’d like to explore the idea of making // syntax be *patterns* instead of simply literals. As a pattern, it should be possible to bind submatches directly into variable declarations, eliminating the need to count parens in matches or other gross things. Here is strawman syntax with a dumb example: if case /([a-zA-Z]+: let firstName) ([a-zA-Z]+: let lastName)/ = getSomeString() { print(firstName, lastName) }let sortedScores: [Int] = ... if case /([\(0)-\(59)]+: let 不可)([\(60)-\(69)]+: let 可)([\(70)-\(79)]+: 良)([\(80)-\(100)]: let 優)/ = sortedScores { print("優: \(優.count)人") print("良: \(良.count)人") print("可: \(可.count)人") print("不可: \(不可.count)人") } a = someValue.someMember someValue.someMember = a mutateParameter(&someValue.someMember)
a = someValue[dynamicMember: "someMember"] someValue[dynamicMember: "someMember"] = a mutateParameter(&someValue[dynamicMember: "someMember"]) (edited)a = [2, 3, 5] a.empty? とか。subscript(dynamicMember:) 内で適宜書き換えてあげないとだめっぽいですね。 (edited)add_trick のままなだけで、 PyVal の subscript で読み替える実装にすれば addTrick にもできるわけですね。 // import DogModule // import DogModule.Dog as Dog // an alternate let Dog = Python.import("DogModule.Dog") // dog = Dog("Brianna") let dog = Dog("Brianna") // dog.add_trick("Roll over") dog.add_trick("Roll over") (edited)SOFT HYPHEN(U+00AD) かな? https://github.com/swift-script/swift-script/pull/18 Apple のドキュメントをコピペすると入ってる時がある。subscript には @_specialize が許可されてない? struct Foo { @_specialize(where T == Int) // OK func foo<T>(_ x: T) -> T { return x } @_specialize(where T == Int) // Error subscript<T>(x: T) -> T { return x } } (edited)subscript は元々ジェネリックじゃなかったけど、ジェネリックになったときに対応忘れたとか? (edited)_ つきだから、 stdlib で使用する要求が無いと実装されないかもしれないですね。@_specialize と言えば、昨日過去最多の @_specialize をつけました・・・。 https://github.com/koher/EasyImagy/blob/dev-0.4.0/Sources/EasyImagy/ImageSpecialized.swift#L2-L677@_specialize の付いてるコードは見つけられないかもしれないwRGBA で倍増して、それを組み合わせたのでこんなことに・・・。 (edited)@inlineable って @_inlineable として既に実装されているような?_ を外して正規仕様にするために提案を整理してるけど内容は現状あるやつ@inlineable ではできないけど、現状 @_versioned でできてるから、これは別の提案で出す、とか。@_versioned はわかりにくいから別の名前が良いって意見があった@_inlineable も最初 @_fragile だった。いずれにしてもパッとは分からないですねー。 1> let a = [2, 3, 5] a: [Int] = 3 values { [0] = 2 [1] = 3 [2] = 5 } 2> let f: (Int) -> Int = a.subscript Segmentation fault: 11 error: value of type '[Int]' has no member 'subscript' let f: (Int) -> Int = a.subscript ^ ~~~~~~~~~
Rintaro Ishizaki added a comment - 15 Sep 2016 4:51 AM Minimum repro: class C { subscript(x: Int) -> Void { return } } _ = C().subscriptsubscript のリファレンスを取る方法は封じられたんでしたっけ? (edited)class C { subscript(x: Int) -> Void { print(x) } } let idx = 12 let kp = \C.[idx] C()[keyPath: kp] keypath はインデックス固定のリファレンスと見れなくもないか? やりたいこと逆ですがw(Int) -> Element として高階関数に渡したいのでpublic class Disposable: DisposableConvertibleにしてユーザー側ではサブクラス作れないようにする (edited)Disposables.create になっているけど、Disposable.init() にしたかったんだよね // this is kind of ugly I know :( // Swift compiler reports "Not supported yet" when trying to override protocol extensions, so ¯\_(ツ)_/¯ /// Optimizations for map operator internal func composeMap<R>(_ transform: @escaping (Element) throws -> R) -> Observable<R> { return _map(source: self, transform: transform) }_map ってどこに定義されてるんだ?public class Disposable { public init() { // ここはクラスクラスタ } } private class CompositeDisposable: Disposable {} // ... その他諸々 protocol DisposableConvertible { var asDisposable: Disposable { get } } public extension Disposable: DisposableConvertible { var asDisposable: Disposable { return self } }_NSArrayCore ふくめ、このファイルの型は Foundation なしの stdlib で Foundation 系の型を扱うためのもの。みたいな感じだと思ってます。 (edited) func bind0<X: EventSourceProtocol>(modelStream: X) where X.Event == Model func bind1(modelStream: EventSource<Model>) (edited)bind1(modelStream: .of(3)) とか書ける事もあって便利bind1(modelStream: Property(3).asEventSource()) 変換が必要EventSource is EventSourceProtocol だから、曖昧なんだけどEventSourceProtocol.of と EventSource.of が曖昧になりそうだ .of() だけ書いてある場合 (edited)protocol MyProtocol { static var foo: Self { get } static var bar: Self { get } } final class MyClass: MyProtocol { static var foo: MyClass { return MyClass() } static var bar: MyClass { return MyClass() } } func func1<X: MyProtocol>(_ arg: X) {} func func1(_ arg: MyClass) {} func1(.foo)おもちprotocolはと解釈したprotocol AnimalProtocol { static func makeNya() -> Cat } ↑は Cat.makeNya() や Dog.makeNya() が呼べる事を示す定義であって、 AnimalProtocol.makeNya() という式で呼べる関数を定義するわけじゃないからいいけどprotocol AnimalProtocol {} class Cat : AnimalProtocol {} extension AnimalProtocol { static func makeNya() -> Cat { return Cat() } } こういう話ではなくて?Float 使いますか? Double 使いますか? Java だと double が普通だと思うんですが。Double ですかね?そもそも指定がなかったらデフォルトで Double 使われますし…Double のみ(もしくは Float のみ)の API ってありますっけ? 1> let a: Float = -3.14 a: Float = -3.1400001 2> abs(a) $R0: Float = 3.1400001var と func と init( で数検索して見たけど func だけFloatが1個少なくて軽く見て見たらFloatのページにだけ abs がなかったけどドキュメント作った人のせいかCGFloat なんですよねぇ。CGFloat は標準ライブラリーで使えなかったっけ?let a = 0.12 print(type(of: a)) // DoubleHashMap の loadFactor みたいなのないかと思ったけど、 https://docs.oracle.com/javase/jp/9/docs/api/java/util/HashMap.html#HashMap-int-float-Dictionary にはなかった・・・。 https://developer.apple.com/documentation/swift/dictionaryDouble を優先して使うのがいいのかな。
char が一遍に8バイト処理できる計算になるけどそんな話聞いたことないですね(聞いたことないだけかもしれないが
Float 優先で使ってたんだろうと考えてみたけど、Float になっていることがUITouch の分解能ってどれくらいなんだろう? (edited)UITouch の分解能が低いなら Float でも合理的か。CGFloat だから、 64bit 環境では 64bit 浮動小数点数になってると思う。Float とか普通かと。GLK_INLINE GLKMatrix4 GLKMatrix4Add(GLKMatrix4 matrixLeft, GLKMatrix4 matrixRight) { #if defined(__ARM_NEON__) float32x4x4_t iMatrixLeft = *(float32x4x4_t *)&matrixLeft; float32x4x4_t iMatrixRight = *(float32x4x4_t *)&matrixRight; float32x4x4_t m; m.val[0] = vaddq_f32(iMatrixLeft.val[0], iMatrixRight.val[0]); m.val[1] = vaddq_f32(iMatrixLeft.val[1], iMatrixRight.val[1]); m.val[2] = vaddq_f32(iMatrixLeft.val[2], iMatrixRight.val[2]); m.val[3] = vaddq_f32(iMatrixLeft.val[3], iMatrixRight.val[3]); return *(GLKMatrix4 *)&m;Float と Double のどちらが使われてるのかということですね。let a = 0.12 の場合 a が Double なので Double の方が一般的かと思いますね Float 使ってたのかを考えると、昔の iOS アプリ開発から惰性でそうしてるだけな気がしてきました。 (edited)public struct CGFloat { /// The native type used to store the CGFloat, which is Float on /// 32-bit architectures and Double on 64-bit architectures. public typealias NativeType = Double これ好きFloat ではなく字面の意味通りのFloatだから(震え声public typealias Float64 = DoubleなのでセーフFloat と Double って名前に統一感ないですよね。せめて Single と Double にするか、 Float32 と Float64 にするかしてほしい。 Swift が悪いわけじゃないけど。Float32 と Float64 作って、 Int みたいな位置の Float ほしい。Unsafe*Pointer<Int32>受ける関数とかありますけどね。 そもそもIntのサイズがネイティブに依存するメリットがいまいち分かっていないです。Int32 だろうが Int64 だろうが成り立つことがほとんどで、Int って書いてるときはオーバーフローは Logic failure だし、バイト数で壊れるようなコードはそもそも Int で書いちゃいけないかと。 (edited)Int32 って書いたコードは未来に Int128 が当たり前になった世界でも Int32 のままになっちゃうよ。Int64 とか Int32 使って、普通の環境は Int でよしなりにプラットフォームに合わせて使わせてくれる方が嬉しいかな派ですねInt32 でも Int64 でも変わらないでしょう派()Int が色々なビット数で動いてくれるとうれしいとは思う。Int32 で足りないシチュエーションって相当レアですよね…int が 32 bit 固定だけど、今の 64 bit 当たり前の時代でも 32 bit に縛られちゃってるのでは?int を long にするとか無理では。List を出したのは例であって、一般的に整数の意味合いで使われてる int がずっと 32 bit のままが辛いということです。int が 32 ビットのまま変わらず 20 年使われてますよ。UITableView のセルの数&インデックスは Int32 だけど Array の要素数&インデックスは Int64 で境界で全部明示的変換が必要ですとか辛くないですか?ArrayList 的なものを想定してると思うけど、 LinkedList とかを考えると話が変わってくるのでは? List は一般的なインタフェースだし。List が動的に要素を計算するとかあり得るのでは?( List のアップデート系のメソッドはオプションのはず)Int から Int32 とかに変換するわけで、その境界で気付くんじゃないかな?普段は Int として扱っていて、ビット数を気にする処理をする際に IntXX に変換で十分だと思うけどなぁ。Int32 とか Int64 とか色分けするのは変だと思うし、それが 32 ビット(に限らない XX ビット)に取り残される原因を生むと思う。size は int なんです。int を全部 long にしましょうとか無茶でしょ?int を 64 ビットにします(もしくは int で書かれてた API の大部分を long にします)とかやったら世界崩壊しそうな気が。 (edited)Int はプリミティブすぎて影響範囲が他よりめちゃくちゃ大きいんじゃないかなぁ。long を使ってたものが、 64 bit の世界では普通にとりあえ使えるようになってるのに、古い仕様にひきずられて永久に面倒な取り扱いを強要されるの辛そう。 (edited)fseek はそんな事になってますねlong なんだけど long が32bitの環境があるので64bit用のオーバーロードがOSによってはオリジナルで提供されているlong は Java の64 bit 整数の意図ね。wchar_t は 2バイト以上」っていうのも同じ問題をおこしましたよlong でポインタをハードキャストしてるところをまた直さないといけないのか・・・IntXX と Int でキレイに分かれてたらそんなにひどいことにはならない気が。Int 、固定したいケースは IntXX でうまくいくと思うんだけどなぁ。UITableView のセル数は概念として一般的な整数だと思うんですよね。ユースケースを考えたら 99% のケースではそれが Int32 に収まるとしても。 (edited)Int32 になって、概念を写し取っているべきだとすると Int になるんじゃないでしょうか。Intっていうのはほとんどのケースには便利だけど、、、という感じですね。Int32 等にすることに(僕が)違和感を感じるのかもしれません。FooProtocol か Foo の foo の実装をコメントアウトすれば動きます。少なくともエラーメッセージはおかしいと思います。 protocol FooProtocol { func foo(_ f: (inout Int) -> ()) } extension FooProtocol { func foo(_ f: (inout Int) -> ()) { var x = 0 f(&x) print(x) } } struct Foo : FooProtocol { func foo(_ f: (inout Int) -> ()) { var x = 42 f(&x) print(x) } } let a = Foo() a.foo { $0 += 1 }
inout-hof.swift:22:9: error: passing value of type 'Int' to an inout parameter requires explicit '&' a.foo { $0 += 1 } ^ & (edited)a.foo { $0 += 1; () } でも動きます。public typealias ArraySlice<T> = Slice<[T]> にするPR (edited)Slice を導入するのは大きな変更では?typealias Substring = Slice<String> (edited)ImageSlice の名前どうしよ。できるだけ ArraySlice を踏襲するようにしてたのに。ImageProtocol を導入して Image と ImageSlice で共通の実装を持たせるとこが今朝大分できたのに・・・。Slice にできないと思う。ArraySlice の名前が残るならそれでもいいけど、Subimage にするか ImageSlice にするかで迷って、 ArraySlice 的なアップデートができるからそっちに寄せて ImageSlice にした。ArraySlice おもしろくて、↓みたいな挙動する。 let a = [2, 3, 5, 7, 11] let b: ArraySlice<Int> = a[1...3] // [3, 5, 7] let c: ArraySlice<Int> = [3, 5, 7] b[1] // 3 c[0] // 3 b == c // true 9> b[b.startIndex..<b.endIndex] $R2: ArraySlice<Int> = 3 values { [1] = 3 [2] = 5 [3] = 7 } 10> c[c.startIndex..<c.endIndex] $R3: ArraySlice<Int> = 3 values { [0] = 3 [1] = 5 [2] = 7 }let a = Image<Int>(width: 4, height: 3, pixels: [ 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, ]) let b: ImageSlice<Int> = image[1...2, 1...1] // [[8, 9]] let c = ImageSlice<Int>(width: 2, height: 1, pixels: [8, 9]) b[1, 1] // 8 c[0, 0] // 8 b == c // true (edited)Slice<T> is T みたいなサブタイピングが追加されそうな予感The downside of having two types is the inconvenience of sometimes having a Substring when you need a String, and vice-versa. It is likely this would be a significantly bigger problem than with Array and ArraySlice, as slicing of String is such a common operation. It is especially relevant to existing code that assumes String is the currency type -- that is, the default string type used for everyday exchange between APIs. To ease the pain of type mismatches, Substring should be a subtype of String in the same way that Int is a subtype of Optional<Int>. This would give users an implicit conversion from Substring to String, as well as the usual implicit conversions such as [Substring] to [String] that other subtype relationships receive.Optional だけでなくさらなる黒魔術を導入しようとしてるのか?Substring is a String 無理だと思うんだけどな。let s = "abc" let t: Substring = s[s.index(s.startIndex, offsetBy: 1)...] // "bc" let i = t.startIndex print(t[i]) // "b" let u = String(t) print(u[i]) // "c"Optional はもう一歩踏み込んでいて、↓ができる。 class A { func foo() -> Int? { return nil } } class B : A { override func foo() -> Int { return 42 } }String と Substring は今はできない。Int も Int? のサブタイプにはなってなくて、↓はエラー。 9> let a: Int? = 42 a: Int? = 42 10> a.map { $0 * 2 } $R0: Int? = 84 11> let b: Int = 42 b: Int = 42 12> b.map { $0 * 2 } error: repl.swift:12:1: error: value of type 'Int' has no member 'map' b.map { $0 * 2 } ^ ~~~String は UnsafePointer<UInt8> に渡せるし。Substring should be a subtype of String in the same way that Int is a subtype of Optional<Int>. let str: String = "hello" strlen(str) // これはだめにして strlen(^str) // こうするみたいな。 (edited)UnsafePointer 周りは & がその役割してるのでは?in the same way that Int is a subtype of Optional<Int>. はもっと強い意味だけど、 the default string type used for everyday exchange between APIs. を解決するのにサブタイプは必要ないんじゃないか?っていう意見です。& も構文失敗してる気がする。↓ややこしい。 func f(_ p: UnsafePointer<Int>) { print(p[0]) } func g(_ p: UnsafePointer<[Int]>) { print(p[0].count) } var a = 42 var b = [2, 3, 5] f(&a) f(&b) g(&b)f(&b) が良くないinternal な後置演算子作って変換すればいいし^ で変換、今もできる。 prefix operator ^ prefix func ^(value: Substring) -> String { return String(value) } let s = "abc" let t: Substring = s[...] let u: String = ^t print(u)strlen(str) // 元のコード // 変換先のコード str.withUnsafeBufferPointer { strlen($0) }strlen(^substr) // 元のコード // 変換先のコード (^substr).withUnsafeBufferPointer { strlen($0) } になるのではなく? (edited)var ary = [2, 3, 5] func f(_ p: UnsafePointer<Int>) { } f(ary) // は ary.withUnsafeBufferPointer { f($0.baseAddress!) } // の sugar (edited)Array の内部のポインタをそのまま取り回せないようにするのがその意図なんじゃない?( Array をぶっ壊したり、ライフサイクルを超えて取り回したりできてしまうから)
This contrasts with Array, which can store its elements in either a contiguous region of memory or an NSArray instance if its Element type is a class or @objc protocol.import Foundation prefix operator ^ prefix func ^(value: Substring) -> String { return String(value) } let s = "abc" let t: Substring = s[...] let u: String = ^t print(u) print(strlen(u)) // OK: 3 print(strlen(^t)) // OK: 3ArraySlice → Array にせよ、変換してから渡すから問題ないのでは?mutating じゃないといけないときはダメだけど。import UIKit protocol ProtocolA {} class CustomCell: UITableViewCell, ProtocolA {} func hoge<T>(cellType: T.Type) where T: UITableViewCell & ProtocolA {} let typeA: (UITableViewCell & ProtocolA).Type = CustomCell.self let typeB: CustomCell.Type = CustomCell.self hoge(cellType: typeA) // error: generic parameter 'T' could not be inferred hoge(cellType: typeB) // ok こういうもんなのかfunc fuga(cellType: (UITableViewCell & ProtocolA).Type) {} fuga(cellType: typeA) // ok fuga(cellType: typeB) // ok ジェネリクスじゃなくしたら行けた。hoge(cellType: CustomCell.self)[UITableViewCell & ProtocolA).Type] に詰めてforEachで全部処理しようとしたら詰まったという感じですAny<Error> が許されるようになれば T: Error に Any<Error> を入れられるようになるのかな??typealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element> が成り立たないか。AnySequence 等の個別の実装をなくせない)だし、 typealias AnySequence<Element> = Any<Sequence where .Iterator.Element == Element> でもそのために早期 ABI 安定化ができないのだとすると、どちらかを諦めるしかなさそうですね。Any<FooProtocol> の形に統一するのがいいんじゃないかと思ってます。Self や associatefdtype を持つプロトコルは型として使えないのに、一部のプロトコルが型として使えることに気持ち悪さを感じてます。protocol は interface ではないので。protocol Functor { associatedtype A func fmap<FB where FB ~= Self>(f: A -> FB.A) -> FB }protocol<P1, P2> が Any<P1, P2> ではなく P1 & P2 に置き換えられちゃったみたいだけど、 generalized existential と整合がとれなくなっちゃってる気がするんだけどどうするんだろう・・・。 https://github.com/apple/swift-evolution/blob/master/proposals/0095-any-as-existential.mdAny<P1, P2> という表記で提案されていたのが最終的に P1 & P2 に変更されて採用されてるんです。The principle problem identified by community about the proposal is that "Any<T1, T2>” implies very strongly an “any of T1 OR T2” relationship
https://lists.swift.org/pipermail/swift-evolution-announce/2016-June/000182.htmlAny<P1 & P2 where ...> になるんじゃないかと思います。P1 & P2 は廃止される?Any<P1 & P2> の sugar として残る(残さざるおえない)んじゃないかなー。P1 & P2 is P1 になるってことですよね?まあゆるくなるだけだから互換性はありますが・・・。Any<P> よりも P が使われそうなのが微妙・・・P を型として書くのは existential であることを意識しないで使ってしまうので廃止してほしい。P を Any<P> と書かないといけなければ、 func foo<T : P>(_ x: T) が望ましいものが func foo(_ x: P) と書かれるケースが減りそう。 func foo(_ x: Any<P>) だと気持ち悪さがあるので。 (edited)class UITableView { var delegate: Any<UITableViewDelegate>? } これは受け入れられるのだろうかid<UITableViewDelegate> って書いてたんだよねprotocol と interface が概念的に違うことについて、公式ドキュメントでちゃんとした説明が必要だ・・・ (edited)Sequence<where Element == Int> みたいな 構文になる可能性もありますね。P1<where ...> & P2<where ...>Sequence<Int> にしないのかみたいな話を誘発しそうです・・・
protocol の static func は必要では? Argo の Decodable とかそれなしに成立しないし。 init は static func と同じだし。static func を持てるのは制約を表すために普通に必要だと思うけどなぁ。たとえば↓とか。 protocol Group { // 群 static func •(lhs: Self, rhs: Self) -> Self // 結合法則を満たす演算 static var identity: Self { get } // 単位元 var inverse: Self { get } // 逆元 } (edited)class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y)interface は protocol とは別の概念だから異なってていいのでは? (edited)Sequence<Element> じゃないの?ってとこだろうし。Sequence<Element> はでも結局不可能でSequence<where Element == Int> にしたとして、なんで Sequence<Int> じゃないの?ってなってSequence<Int, _, _>みたいにできるが…?protocol Sequence<Element> { ... } とはできないからやっぱり混乱するだけじゃないかなぁ。<>が出てくるから似た機能のGenericsが想起されて混乱する (edited)[ ] はArrayに使っちゃったしなあSequence[where .Element == Int]let array: [Sequence[where Element == Int].Type] = [Sequence[where Element == Int].self] んー、なんとかいけるか?let a: Int[] = [1] は未だに Fix-it 出してくれるという親切さ。Int[] の時代とかありましたね。 Java から来るとわかりやすかったけど、今となっては何がよかったのか・・・AnyIterator は struct なんだけど、値型捨て過ぎなのでもう class になった方がいいのでは? let a = [2, 3, 5] let i = AnyIterator(a.makeIterator()) let j = i print(i.next()) // Optional(2) print(j.next()) // Optional(3)struct なのに let で next できてることからも。next が mutating func じゃないという潔さ。class にしなかった意味がよくわからない。beginAsync has the following signature. func beginAsync(_ body: () async throws -> Void) rethrows -> Void However, I think it is better to forbid body to throw errors, that is to say, to change its signature to the followi...async -> Never? Theoretically, async -> Void means you're awaiting a result with only one possible value, but if you're not waiting at all, then there is truly no result, yes? ··· On Sun, Nov 12, 2017 at 9:27 AM, Yuta Koshizawa via swift-evolution < swift-evolution@swift.org> wrote: Sorry, I had got s...Norio_Nomuraになってしまってモヤモヤする…Username Yuta_Koshizawa People can mention you as @Yuta_Koshizawaomochimetaru なんですけどomochi.metaru になっていて@ の左の omochi.metaru になった。username 変更しちゃうと今までの @ と紐付かなくて困るって話です? (edited)Problems happen when someone who have posted mails to the ML in the past sign up using GitHub authentication. They hope to change their username, but cannot do it after once their accounts were created. I wish there were some additional explaination in the sign up screen, and to get a chance to change the username even if signup is done using github authentication.norio_nomuraにしておこうかな…joined にリネームされました。 [[2, 3], [5, 7, 11]].joined()joined() の文字見た気がするのに、無視してました...Array を用いるケース自体が稀な気がしますlet array: [Any] = [1, 2, 3, [4, 5, 6], [7, [8, 9]]]JSONSerialization でパースすれば↑のような Array が得られるでしょうが、どのようにネストされているか不定なネストした Array を処理するようなことは実用上ほぼないんじゃないでしょうか。Array をすべてつぶして flatten したいようなケースはあると思います。[[[1], [2, 3]], [[4, 5, 6]]] → [1, 2, 3, 4, 5, 6] とか。これならキレイに書く方法がありそうです。Data の write(to:options:) に倣って名詞を後ろにおいてもいいだろうということで write(to:formatting:) → write(to:format:) としました。ありがとうございました。 (edited)extrapolatedBy: を付与することで、 x, y が画像の範囲外にはみ出てしまった場合でも外挿してくれる API を提供しています。 let red = RGBA<UInt8>(red: 255, green: 0, blue: 0, alpha: 255) let pixel = image[x, y, extrapolatedBy: .filling(red)] // 例えば x = -1, y = -1 なら red が返される (edited)extrapolatedBy に与えているのは ExtrapolationMethod 型の値なのですが、次のように宣言されています。 enum ExtrapolationMethod<Pixel> { case filling(Pixel) // 画像の外側を与えられた値で埋める case edging // 画像の外側を最も近いエッジのピクセルで埋める case repeating // 画像の外側を画像の繰り返しで埋める case reflecting // 画像の外側を画像を鏡写しに折り返して埋める } (edited)edge, repeat, reflection に変えた方がいい気がしてるんですがいかがでしょうか。 (edited)edging を縁取り的な意図で使ってるんですが、無限の大きさを持った縁なので 縁取りのイメージと合わない気が。一方で、今のままだと -ing の統一感はあります。clampingToEdge とかになると思うんですがちょっと長すぎかなと。extrapolatedBy につながるので、 edge でも意味は通るかなぁとは思うのですが、悩ましいですね・・・。 repeating と reflecting は repeat, reflection にします。 filling は -ing でも変じゃないですよね? (edited)repeat が予約語だったから repeating にしたんだった・・・。loop とかかなぁ。.repeat では済むのか。repeat の方が一般的かな? CSS や GL も repeat だし。.repeat にします。GL_TEXTURE_WRAP_S Sets the wrap parameter for texture coordinate ss to either GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT, GL_REPEAT, or GL_MIRROR_CLAMP_TO_EDGE. constant かぁ。確かに filling(Pixel) → constant(Pixel) にしてもいいかも?extrapolatedBy: .constant(red) とか。 (edited)ExtrapolationMethodがconstantだとちょっと違和感ある気がしますね。 PaddingがconstantだとしっくりくるんですがextrapolatedBy を paddingBy にした方がいいかな?interpolatedBy とそろえたかったんだけど。paddedBy かな?interpolatedBy は .nearestNeighbor とか .bilinear とかが続く。padding というタームは浸透してるから paddingBy がいいけど、そうすると interpolatingBy も検討しないといけない・・・。resizedTo(width: ..., height: ..., interpolatedBy: ...) とかもあるからなぁ。enum ExtrapolationMethod<Pixel> { case filling(Pixel) // 画像の外側を与えられた値で埋める case edging // 画像の外側を最も近いエッジのピクセルで埋める case repeating // 画像の外側を画像の繰り返しで埋める case reflecting // 画像の外側を画像を鏡写しに折り返して埋める }InterpolationMethod です。subscript(x: Double, y: Double, interpolatedBy interpolationMethod: InterpolationMethod, extrapolatedBy extrapolationMethod: ExtrapolationMethod<Pixel>) -> Pixelsubscript のラベルは名詞を並べるだけで良さそうだから https://developer.apple.com/documentation/swift/dictionary/2894528-subscriptsubscript(x: Double, y: Double, interpolation: InterpolationMethod, extrapolation: ExtrapolationMethod<Pixel>) -> Pixel とか、 extrapolation → padding とかでいいかもだけどExtrapolationMethod を改造かなと思ってる。ExtrapolationMethod の case にも .repeat とかを用意しておいて .all(.repeat) の意味になるとか。で、 .all(.all(...)) とかはダメだから、 .repeat だけを分離した型も必要になる。.bilinear で横は nearestNeighbor で、とかあり得るよね。resizedTo の話ね。 (edited)InterpolationMethod と ExtrapolationMethod はそのままで、必要に応じて DirectedInterpolationMethod とかを追加するのが良さそう。 .both(.bilinerar), .each(x: .nearestNeighbor, y: .bicubic) とか。Dictionary の subscript[_:default:] に倣うなら subscript[_:_:interpolation:extrapolation:] とかかなぁ。resizedTo(width:height:) のラベルが難しい・・・DirectedInterpolationMethod でカバーできる想定で。enum じゃなくて sealed class だったら階層的にサブタイピングでうまいことわけられるんだけどね。InterpolationMethod SimpleInterpolationMethod NearestNeighbor Bilinear Bicubic DirectedInterpolationMethod Both(SimpleInterpolationMethod) Each(SimpleInterpolationMethod, SimpleInterpolationMethod)enum よりはキレイに整理できそう。open でない public なクラスを使えば Swift でも同じようなことはできる。resizedTo(width:height:interpolation:) は変な気がするけどなぁ。 interpolatedBy とかじゃなくても、 write(to:format:) と同じで別に気にしなくていい? (edited)To の後に width, height, interpolation が並列に並ぶのも気持ち悪い。resized(to: Size, interpolatedBy: InterpolationMethod) とすることもできるけど、そうすると Size の扱いが面倒。(Int, Int) の点に対して補間するだけでは?.filling はダメとか。さらに階層が増える・・・。.repeat とかと概念的に違うんだよなぁ。Bool とか)は .nearestNeighbor の境界上と同じようにどちらかに寄せるとかかな。.custom も考えてる。image[-100...100, -100...100, extrapolatedBy: .custom(...)]InterpolationMethod とかに case custom を追加する話じゃないの? (edited).custom((Double, Double) -> Pixel) と .custom((Int, Int) -> Pixel) を追加するだけなので大変ではない。.custom((Double, Double, (Int, Int) -> Pixel) -> Pixel) にしないとダメそう。(Int, Int) -> Pixel が要らないんだよな。.custom は保留しよう・・・(Int, Int) -> (Int, Int) にできるしね。.filling 以外はこれで表現できる。.filling のために ExtrapolationMethod がジェネリックになってるのもややこしいといえばややこしい。interpolation との組み合わせ爆発もするし。enum じゃ階層にしてすべて ExtrapolationMethod のサブタイプとかもできないしね。Int だったら 0 とかそういうこと?protocol を public にしないといけない・・・。enum Foo { case foo case bar case piyo(String) case fuga(Int) static var specificFuga1 = Foo.fuga(1) } Foo.specificFuga1 こんな感じかなPixel プロトコルを作ってしまうとUInt8 とか Float にプロトコルを後付しないといけないしRGBA<Channel> とかは付けれないし RGBA<UInt8> とかだけに付けるには conditional conformance がいる。_Summable っていうプロトコルを持っていて_Summable が zero みたいなのを持っててroteated(by:) では斜めになったときの背景を zero で埋めてる。( gyb で各型ごとに extrapolatedBy: .zero) を付与して呼ぶメソッドを大量生成してる。)rotated(by:) は使えなくてrotated(by:extrapolatedBy:) じゃないと使えない。rotated の interpolatedBy 省略で .bilinear が使われるけど、それ以外では .nearestNeighbor が使われるとかなってる。.nearestNeighbor ですらなくて、それ相当の計算だけど。 InterpolationMethod が使えるのは _Summable だけだから。( Bool に .bilinear しようがない) (edited)Numeric でもいけなくて、 UInt8 とか普通に足して平均とったらオーバーフローするからInt や Double に変換する処理とかがあって、そのあたりが全部 _Summable で抽象化されてる。複雑・・・。AnyImage も元は要らなかったんだけど、 image[-100...100, -100...100, extrapolatedBy: ...] の結果を ImageSlice で返そうとすると、 ImageSlice が内部に持つのが Image だけじゃダメになって、 ImageSlice が AnyImage を持つ必要に迫られた。internal なメソッドでも @abiPublic でインライン化できるのか。let double: Double = 3.123456 // 3.123456 var dec1 = Decimal(double) // 3.123456000000000512 var dec2: Decimal = Decimal() NSDecimalRound(&dec2, &dec1, 4, .bankers) dec2 // 3.1235 隠蔽してみたextension Decimal { func rounded(exponent: Int, roundingMode: RoundingMode = .bankers) -> Decimal { var me = self var result = Decimal() NSDecimalRound(&result, &me, exponent, .bankers) return result } } dec1 // 3.123456000000000512 dec1.rounded(exponent: 4) // 3.1235NSDecimalNumber, an immutable subclass of NSNumber, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.Decimal は NSDecimal で c 構造体 みたいです。let dec1 = Decimal(double) // 3.123456000000000512 let n = (dec1 as AnyObject) type(of: n) // __ObjC.NSDecimalNumber.Type_ObjectiveCBridgeable っていう ReferenceConvertible の一個下のレイヤーに準拠してますね。let double1: Double = 123456789000000000 let double2: Double = 1 print(String(format: "%.20f", double1)) print(String(format: "%.20f", double2)) print(String(format: "%.20f", double1 + double2)) let decimal1 = Decimal(string: "123456789000000000")! let decimal2 = Decimal(string: "1")! print(decimal1) print(decimal2) print(decimal1 + decimal2)123456789000000000.00000000000000000000 1.00000000000000000000 123456789000000000.00000000000000000000 123456789000000000 1 123456789000000001let decimal1 = Decimal(string: "123456789012345678901234567890")! let decimal2 = Decimal(string: "0.0000000000000000000000000001")! print(decimal1) // 123456789012345678901234567890 print(decimal2) // 0.0000000000000000000000000001 print(decimal1 + decimal2) // 123456789012345678901234567890let decimal1 = Decimal(string: "123456789012345678901234567890")! let decimal2 = Decimal(string: "0.0000000000000000000000000001")! print(decimal1) // 123456789012345678901234567890 print(decimal2) // 0.0000000000000000000000000001 let decimal3 = decimal1 + decimal2 print(decimal3) // 123456789012345678901234567890 print(decimal1 == decimal3) // true!let decimal1 = Decimal(string: "1234567890123456789012345678901234567890")! let decimal2 = Decimal(string: "1")! print(decimal1) // 1234567890123456789012345678901234567890 print(decimal2) // 1 let decimal3 = decimal1 + decimal2 print(decimal3) // 1234567890123456789012345678901234567890 print(decimal1 == decimal3) // true! fileprivate var __exponent: Int8 fileprivate var __lengthAndFlags: UInt8 fileprivate var __reserved: UInt16 public var _mantissa: (UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16, UInt16) 160bitimport Foundation print(MemoryLayout<Decimal>.size) // 20let decimal = Decimal(string: "1234567890")! decimal._exponent // 1 decimal._length // 2 decimal._isNegative // 0 decimal._isCompact // 1 decimal._reserved // 0 decimal._mantissa // (.0 52501, .1 1883, .2 0, .3 0, .4 0, .5 0, .6 0, .7 0)Decimal.greatestFiniteMagnitude // 3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 Decimal.greatestFiniteMagnitude._mantissa // (.0 65535, .1 65535, .2 65535, .3 65535, .4 65535, .5 65535, .6 65535, .7 65535) Decimal.leastNonzeroMagnitude // 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 Decimal.leastNonzeroMagnitude._mantissa // (.0 1, .1 0, .2 0, .3 0, .4 0, .5 0, .6 0, .7 0) (edited)func truelyLeastDecimal() -> Decimal { var x = Decimal(string: "0.1")! var r = Decimal() NSDecimalPower(&r, &x, 128, .plain) return r } func p(_ d: Decimal) { print("=====") print(d) print(d._mantissa) print(d._exponent) } p(Decimal.leastNormalMagnitude) p(truelyLeastDecimal()) /* ===== 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 (1, 0, 0, 0, 0, 0, 0, 0) -127 ===== 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 (1, 0, 0, 0, 0, 0, 0, 0) -128 */ Decimalには定義みあたらないけど、NSDecimalNumberのexponentは Decimal.leastNormalMagnitude > truelyLeastDecimal() こうなってしまうのでCustomStringConvertible じゃないものでも文字列に埋め込めるの良くなくないですか?何でもとりあえず文字列にしたいニーズはあるとして、それはデバッグ用にそういう関数を用意すれば良いだけに思います。 https://qiita.com/koher/items/6c855ddbda8797af4605CustomDebugStringConvertible を分けてる意味がなくなっちゃってる気が。 print が Any をとれるのも微妙。Optional は CustomDebugStringConvertible だったと思うからString.init(describing: Any) だったはずなんでString.init(describing: x) って書けばいいんじゃねえかと"\debug() " みたいなのでもいいけど 1> let a: Int? = 42 a: Int? = 42 2> a.debugDescription $R0: String = "Optional(42)" 3> a is CustomStringConvertible $R1: Bool = true 4> a is CustomDebugStringConvertible $R2: Bool = true 5> a.description error: repl.swift:5:1: error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'? a.description ^ ? 5> func debugPrint<T: CustomDebugStringConvertible>(_ value: T) { print(value) } 6> debugPrint(a) Optional(42) 7> func safePrint<T: CustomStringConvertible>(_ value: T) { print(value) } 8> safePrint(a) error: repl.swift:8:11: error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'? safePrint(a) ^ (edited)Optional は CustomStringConvertible ではないけど CustomDebugStringConvertible ではある(↑)。 (edited)CustomDebugStringConvertible だけ書いてある。 https://developer.apple.com/documentation/swift/optional (edited)extension Optional : CustomDebugStringConvertible where Wrapped : CustomDebugStringConvertible { ... }String.init(describing) で変換すればいいのか。 (edited)public init<T: TextOutputStreamable> (stringInterpolationSegment expr: T) public init<T: CustomStringConvertible> (stringInterpolationSegment expr: T) public init<T: TextOutputStreamable & CustomStringConvertible> (stringInterpolationSegment expr: T) (edited) public init<T>(stringInterpolationSegment expr: T) { self = String(describing: expr) } CustomDebugStringConvertibleのケースはこれに行きそう (edited)is で true はバグな気がする。 8> let b: CustomStringConvertible = a error: repl.swift:8:34: error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'? let b: CustomStringConvertible = a ^ ! 8> let c: CustomDebugStringConvertible = a c: CustomDebugStringConvertible = { payload_data_0 = 0x000000000000002a payload_data_1 = 0x0000000000000000 payload_data_2 = 0x0000000000000000 instance_type = 0x0000000101416700 libswiftCore.dylib`InitialAllocationPool + 80 protocol_witness_0 = 0x00000001013d1b78 libswiftCore.dylib`protocol witness table for <A> Swift.Optional<A> : Swift.CustomDebugStringConvertible in Swift }String(describing:) か debugDescription すればいいんだから、直接埋め込める必要はない気がします。あえて言えば初心者が混乱するくらい??String(desciribing:) 使ったら?」って出せばいいからそこはダイジョブそう
enum に偏りすぎな気が。 https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.mdBool は ValueEnumerable でいいし、 ValueEnumerable な associated value を持つ case があってもその enum は ValueEnumerable でいい気がする。enum Foo { case bar case baz(Bool) } は ValueEnumerable になりたかったりしないかな・・・。The compiler will synthesize an implementation of ValueEnumerable for an enum type if and only if: - the enum contains only cases without associated values; - the enum is not @objc; - the enum has an explicit ValueEnumerable conformance (and does not fulfil the protocol's requirements).ValueEnumerable のときは生成で良い気が。 Codable が Codable なプロパティを持っている場合と同じように。Foo の ValueCollection が得られれば良いのでは?for x in Foo.allValues { } みたいに書けるようになってもif x == .bar { ... } if case .baz(false) = x { ... } こういう感じか。struct Card : ValueEnumerable { let suit: Suit // Suit : ValueEnumerable let number: Number // Number : ValueEnumerable } Card.allValues (edited)Card.ValueCollectionTo limit our scope, we are primarily interested in simple enums—those without any associated values—although we would also like to allow more complicated enums and structs to manually participate in this mechanism.Int に精度を明示すべきかどうかみたいな話とも関係してるんじゃないかなぁ。 Int は ValueEnumerable であってほしくないけど(概念としての整数は無限の要素を持つので) UInt8 は ValueEnumerable であってほしい。同様に考えると Int32 や UInt32 も ValueEnumerable であってほしいけど、 Int を associated value に持つ enum に ValueEnumerable であってほしいわけじゃなくて、 Int と Int32 の使い分けはやっぱり必要な気がする。Never の bottom 化早く・・・
allValues ができる Int64 とできない Int を区別しておきたいですねぇ。Int64 だと count が Int で表せないという話は別として)Int32 か Int64 かの選択が揺らぐようなのをなくしたいです。Int32 等に変換して使いたいイメージですね。Int にはビット演算とかオーバーフローとかもしたくないです。IntXX でやって、アプリ用に Int を使った型に変換するのか、それともエンティティレベルで Int にして、 DB に保存するときにエラーにしてしまうのか。Int に Java の BigInteger とか Ruby の整数のような無限精度になってほしいわけではないですが、コードの書き手が単なる整数を意図しているのかそうではないのかを型として区別しておきたいという感じですね。UInt8 だとオーバーフローを利用した計算とかもよくやると思うんですけど、 Int32 ではそれは OK だけど Int ではやりたくないです。そこは明示的に Int32 に変換してから処理したいです。Int が実行環境によって精度が変わるかどうかは別として、 Int32 や Int64 より strict な型がほしいという感じですね。Int が 32 bit に固定されていたとしても、 Int に対してオーバーフローを利用した演算はしたくないです。Intが無限長で有限長を使いたいならInt32然りInt64を使う、というのが個人的には一番しっくり来るInt が現実的に 32bit や 64bit なのには不満はないです。無限精度にはなってほしくない。ValueEnumerable の例で言えば func combination<T: ValueEnumerable, U: ValueEnumerable>(_ a: T.Type, _ b: U.Type) -> [(T, U)] みたいな関数があったときにUInt8 や Int32 や Int64 は ValueEnumerable だけど Int は違ったらcombination(Int, Bool) みたいなのを防げます。combination(UInt8, Bool) はやりたいですし、 combination(Int32, Bool) も同様に認められるべきだと思います。Int に BigInt (無限精度)になってほしいわけではなくて、今のままであってほしいと思います。Int は単に整数であってほしいだけだと思うので、基本的には制限された Int を使ってInt32 特有の操作をするときだけ明示的に Int32 に変換して使えたらいいんじゃないかと思います。Int は最もよく利用する型なのでパフォーマンスも求められると思います。 BinaryInteger として existential で扱うのは許容できないかと。たとえば、 count は Int を返しますが、これは整数を表していると思います。しかし、これが BinaryInteger になるのは許容できないのではないかと。Int や UInt に ~ が使えるのとかは違和感を感じますね。 1> let n: UInt = 0 n: UInt = 0 2> ~n $R0: UInt = 18446744073709551615 3> ~(UInt64(n)) $R1: UInt64 = 18446744073709551615 とすべきかと。Int のビット数を固定しなかったのが良くなかったという意見なのに対して、僕はそれでよかったと思ってるところでしょうか。そして、その裏には、 Int は整数だからという気持ちがあります。Int のビット数が固定されないことで、↑のようなビット演算が良くない行為となり、明示的な変換が求められます。ただ、もう少し踏み込んで Int には ~ や &+ 等はなくてよかったと思います。Int はほぼ最大値を気にしなくてよい整数なんですよね。 BigInteger が無限精度だと言っても実際には無限ではなくて、↓のような計算をすると精度が足りません。 $ irb irb(main):001:0> 2 ** (2 ** (2 ** (2 ** (2 ** 2)))) (irb):1: warning: in a**b, b may be too big => Infinity (edited)BigInteger で何であれ気にしないといけない、かもしれません。 (edited)Int だろうが BigInteger だろうがあまり関係なくどちらも整数と思えるという感じです。2 ** (2 ** (2 ** (2 ** 2))) と入力すると楽しいことになります。irb(main):012:0> a.class => Bignum irb(main):013:0> a = 2 ** a (irb):13: warning: in a**b, b may be too big => Infinity irb(main):014:0> a.class => Float2 ** (2 ** (2 ** (2 ** (2 ** 2)))) (edited)BigInteger の限界を突破するということです。BigInteger が必要そうですねぇ。AnyIterator が Sequence なのってなんでですか? https://developer.apple.com/documentation/swift/anyiterator
for x in (AnyIterator { hoge() }) {} これができたら楽Sequence としての責務を果たせてない気が。 let i = AnyIterator([2, 3, 5].makeIterator()) let i2 = i.makeIterator() print(i.next()) // 2 print(i2.next()) // 3 let i3 = i.makeIterator() print(i3.next()) // 5AnySequence { AnyIterator { hoge() } }になるのかなIteratorProtocol : Sequence もほしくならない?Sequence と IteratorProtocol の責務が同じにならない?struct Countdown: Sequence, IteratorProtocol { var count: Int mutating func next() -> Int? { if count == 0 { return nil } else { defer { count -= 1 } return count } } }IteratorProtocol との役割分担がよくわからない・・・。extension Array { /// Sequence of all sub sequences of specified `length`. public func subSequences(of length: Int) -> SubSequenceSequence<Element> { return SubSequenceSequence(array: self, length: length) } みたいなのを書いてたんですがSubSequenceIteratorをSequenceにしてそれを返すのも良いのか。Iteratorを返すと複数回呼んだ時に使用中のが返ってきたりする可能性も読み取れるような。 でもSequecne自体も複数回走査が保証されてないなら……Sequence が makeIterator で消費されることがありなら、 extension IteratorProtocol { func makeIterator() -> Self { return self } } 的な実装が可能で、すべての IteratorProtocol の実装は Sequence として振る舞えることになる。protocol MySequence { associatedtype Iterator: MyIterator func makeIterator() -> Iterator } protocol MyIterator: MySequence { associatedtype Element func next() -> Element? } extension MyIterator { func makeIterator() -> Self { return self } }
MyPlayground.playground:3:20: Type may not reference itself as a requirement 実装都合な気もしてきたIteratorProtocol は Sequence でないけど AnyIterator が Sequence である理由はなんでしょうか?protocol MySequence { associatedtype Element associatedtype Iterator: MySequence where Iterator.Element == Element func makeIterator() -> Iterator func next() -> Element? } こういう風にできると思うAnyIterator が Sequence なのに IteratorProtocol が Sequence じゃない理由がない?AnySequenceにinit追加したら良いんじゃと思うんですがどうでしょう? extension AnySequence { init(iterator: @escaping ()->Element?) { self.init { return AnyIterator(iterator) } } }Sequence がイテレータ経由で consume されるのも微妙な気が。
for で回したいんだったら for in に T: IteratorProtocol を許容するようにした方がシンプルな気もするけどなぁ。map とかもできるのかと思ったら単に AnyIterator が Sequence だからできるだけだった。AnyItearator については使い捨てで操作したいこともあるし、かといって AnyIteratorAnySequence で包むのはオーバーヘッドがあるからしかたないのかなぁ。 (edited)AnySequenceで包むのは〜の誤りですか?Sequenceとして不適当な場合があるのかってことですねlet a = [0, 1, 2] var i1: IndexingIterator<[Int]> = a.makeIterator() print(i1.next()) // 0 var i2: IndexingIterator<[Int]> = i1.makeIterator() print(i2.next()) // 1 再列挙してないみたいです (edited)let a = [0, 1, 2] var i1: IndexingIterator<[Int]> = a.makeIterator() print(i1.next()) // 0 var i2: IndexingIterator<[Int]> = i1.makeIterator() print(i2.next()) // 1 print(i1.next()) // 1 分岐はしているみたいIndexingIteratorのmakeIteratorで途中状態を考慮したIteratorが帰ってくるというのは別に悪くないようにも思えますねぇIndexingIterator は意図がよくわかんないけど、Zip2Iterator は ZipSequence 経由で生成するから、逆に Iterator から Sequence を作りたいユースケースがないんじゃない?AnyIterator の場合は Sequence を作りたくなるユースケースが想定されて、しかも AnySequence でラップするには無視できないオーバーヘッドがあって仕方なく Sequence になってるのかと。IndexingIterator はわかんない。AnySequence で包んでから使う方がいいと思うけど。 (edited)IteratorProtocol と Sequence を混ぜるのは余計混乱するのでは?AnyIterator の話じゃないのか。Sequence は基本的には makeIterator を複数回実行できることを想定しながらも、 consume するものにも使えるというのはまあいい気がしてきた。initの裏技(by @lovee )、自身の型を返してサブクラス作るとセグフォ出ますね。class My { static func `init`(arg: Void) -> My { return MyMy() } } class MyMy: My { } My(arg: ()) (edited)observable.my.flatMapみたいなmy 無くてこっちはあるとかそんなんは地獄っぽいのでアンスコがコスパ良いかなーって感じするlet a = [0: true, 1: false] // Error: `filter` is ambiguous // let x = a.filter { $0.value }.sorted(by: { $0.key < $1.key }) // Equivalent let x1 = a.filter { $0.value } let x2 = x1.sorted(by: { $0.key < $1.key}) 4からDictionaryのfilterが二種類になったみたいで、 分割して書いた場合は優先順位が付くけどsortedが続いた場合は型推論で両方通るのでambiguousになる?みたいですlet x = (a.filter { $0.value } as Array).sorted(by: { $0.key < $1.key }) ソートを先にするとかで対応できますがいまいち……sortedがついた場合も優先順位働かせることはできそうですけど やっぱ中間の型が隠れてるのは良くないとかいう判断なんでしょうかね。 (edited)sortedだったのでそのまま書きましたがcountの例のほうがより問題に近かったですね。values に対する filter だから違うか。countだと通るというよく分からない状態 に let a = [0: true, 1: false] let xx = a.filter { $0.value }.countenum E { case A(Int, String) case B(String, Float) } func foo(e: E) { switch e { case .A(_, let str): // something. fallthrough case .B(let str, _): print(str) } } これが通るようになるらしいです。 https://github.com/apple/swift/pull/14041/files#diff-3f455e97fae02f7b158f90077919c17fR140String の < はこれで実装されているわけではない? This method implements the mathematical notion of lexicographical ordering, which has no connection to Unicode. If you are sorting strings to present to the end user, use String APIs that perform localized comparison.String の < の順序変わるの??Character の順序やんね?Character の順序が (a|A) < (b|B) になってたらString の lexicographicallyPrecedes もその順になるのでは?$ swift Welcome to Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2). Type :help for assistance. 1> let a: Character = "a" a: Character = { _representation = smallUTF16 { smallUTF16 = 97 } } 2> let b: Character = "B" b: Character = { _representation = smallUTF16 { smallUTF16 = 66 } } 3> a < b $R0: Bool = false (edited)var ss = ["apple", "Apple", "banana"] ss.sort() print(ss) // "["Apple", "apple", "banana"]\n" ss.sort { (a, b) in a.lexicographicallyPrecedes(b) } print(ss) // "["Apple", "apple", "banana"]\n" @_inlineable @_versioned internal static func compare( _ left: _StringGuts, to right: _StringGuts ) -> Int { defer { _fixLifetime(left) } defer { _fixLifetime(right) } #if _runtime(_ObjC) // We only want to perform this optimization on objc runtimes. Elsewhere, // we will make it follow the unicode collation algorithm even for ASCII. // This is consistent with Foundation, but incorrect as defined by Unicode. // // FIXME: String ordering should be consistent across all platforms. if left.isASCII && right.isASCII { let leftASCII = left._unmanagedASCIIView let rightASCII = right._unmanagedASCIIView let result = leftASCII.compareASCII(to: rightASCII) return result } #endif return _compareDeterministicUnicodeCollation( _leftUnsafeStringGutsBitPattern: left.rawBits, _rightUnsafeStringGutsBitPattern: right.rawBits) }// FIXME: String ordering should be consistent across all platforms.String の順序とかぐちゃぐちゃになってそう・・・ /// Compares two slices of strings with the Unicode Collation Algorithm. @inline(never) // Hide the CF/ICU dependency @effects(readonly) public // @testable static func _compareDeterministicUnicodeCollation( 3> ["apple", "Apple", "banana", "Banana"].sorted() $R2: [String] = 4 values { [0] = "Apple" [1] = "Banana" [2] = "apple" [3] = "banana" }var ss = ["apple", "Apple", "banana", "Banana"] ss.sort() print(ss) // ["Apple", "Banana", "apple", "banana"]\n" ss.sort { (a, b) in a.lexicographicallyPrecedes(b) } print(ss) // ["Apple", "Banana", "apple", "banana"]\n"lexicographicallyPrecedes 的な?/// Compares the strings via the Unicode Collation Algorithm on the root locale. /// Results are the usual string comparison results: /// <0 the left string is less than the right string. /// ==0 the strings are equal according to their collation. /// >0 the left string is greater than the right string. int32_t swift::_swift_stdlib_unicode_compare_utf16_utf16(const uint16_t *LeftString, int32_t LeftLength, const uint16_t *RightString, int32_t RightLength) { // ICU UChar type is platform dependent. In Cygwin, it is defined // as wchar_t which size is 2. It seems that the underlying binary // representation is same with swift utf16 representation. // On Clang 4.0 under a recent Linux, ICU uses the built-in char16_t type. return ucol_strcoll(GetRootCollator(), reinterpret_cast<const UChar *>(LeftString), LeftLength, reinterpret_cast<const UChar *>(RightString), RightLength); } (edited) 4> ["👨👩👧👦", "👨", "👩"].sorted() $R3: [String] = 3 values { [0] = "👨" [1] = "👨👩👧👦" [2] = "👩" }U+1F468 U+200D U+1F469 U+200D U+1F467 U+200D U+1F466 (edited)Language Swedish: z < ö German: ö < z Usage German Dictionary: of < öf German Phonebook: öf < of Customizations Upper-First A < a Lower-First a < ALanguage Swedish: z < ö German: ö < z Usage German Dictionary: of < öf German Phonebook: öf < of Customizations Upper-First A < a Lower-First a < ATable 2. Comparison Levels Level Description Examples L1 Base characters role < roles < rule L2 Accents role < rôle < roles L3 Case/Variants role < Role < rôle L4 Punctuation role < “role” < Role Ln Identical role < ro□le < “role”Table 4. Context Sensitivity Contractions H < Z, but CH > CZ Expansions OE < Œ < OF Both カー < カア, but キー > キアカー < カア, but キー > キア これ確かに日本語ネイティブだから意識してないけどヤバイな。var ss = ["カア", "カー", "キイ", "キー"] ss.sort() print(ss) // ["カア", "カー", "キイ", "キー"] ss.sort { (a, b) in a.lexicographicallyPrecedes(b) } print(ss) // ["カア", "カー", "キイ", "キー"]puts "カー" < "カア" # false puts "キー" < "キア" # falsepublic func _compareStringsPreLoop( selfUTF16: UnsafeBufferPointer<UInt16>, otherUTF16: UnsafeBufferPointer<UInt16> ) -> _Ordering {private func _compareStringsPathological( selfUTF16: UnsafeBufferPointer<UInt16>, otherUTF16: UnsafeBufferPointer<UInt16> ) -> _Ordering { var selfIterator = NormalizedIterator(selfUTF16) return selfIterator.compare(with: NormalizedIterator(otherUTF16) ) } (edited)internal func _tryNormalize( _ input: UnsafeBufferPointer<UInt16>, into outputBuffer: UnsafeMutableBufferPointer<UInt16> ) -> Int? { var err = __swift_stdlib_U_ZERO_ERROR let count = __swift_stdlib_unorm2_normalize( _Normalization._nfcNormalizer, input.baseAddress._unsafelyUnwrappedUnchecked, numericCast(input.count), outputBuffer.baseAddress._unsafelyUnwrappedUnchecked, numericCast(outputBuffer.count), &err ) guard err.isSuccess else { // The output buffer needs to grow return nil } return numericCast(count) }> @dabrahams Sorry, I didn't read the string manifesto. > Hm, it seems that the string manifesto says "Swift string sorting will not be compatible with > Foundation sort order". > Is that correct with that understanding? Yes, non-localized String sorting will not work the same way in Swift as it does in Foundation. We intend to try very hard to keep localized sorting in sync, though. なんか詳細忘れちゃったな…norio_nomura [12:54 PM] ASCIIに関しては一部の記号の扱いがFoundationとUnicodeで違うぽい。 [12:54 PM] で、大文字小文字のどちらが前に?ってのは、Unicodeでも「カスタマイズできるようにするべき」って書かれてる。 http://unicode.org/reports/tr10/#Case_Comparisons (edited)@noescape 懐かしいstruct Cat : CustomStringConvertible { var name: String var description: String { return name } } var a: Cat = .init(name: "たま") var b: Cat? = .init(name: "くろ") // [1] print("\(a)") // [2] print("\(b?.description ?? "")") // [1] にたいして [2] の記述量が多く理不尽に感じる。 // 「noneなら空文字列」という事だけが言いたいだけなので、 // 例えば以下のように書きたい。 print("\(b ?? "")") // でもこれは ?? の両辺の型としておかしいので・・・ // 例えば \() を2引数関数のようにみなして print("\(b, "")") // などと書けるようにするとか?Optional(2018) から思っている事↑\(b, defaultValue: "") の方がそれっぽいかなあ。 (edited)print("\(b, "")") は現状でも書けちゃうという。 "\((b, ""))" と解釈される。.description になるか、 .debugDescription になるか、 (3) になるか (4) になるか\(a) ってかいてるときは気にする必要が無いのに (edited)Optional where Wrapped: CustomStringConvertibleなXにX ?? Stringのオペレーターを追加するとスッと書けそうfoo?.description ?? "" は必要最小限の表記な気が。foo.description(default: "") くらい? (edited)"\(foo)" だけで書けるのに、というモチベーションは変わらないですねえsubscript(key: Dictionary.Key, default defaultValue: @autoclosure () -> Dictionary.Value) -> Dictionary.Value { get set }
https://developer.apple.com/documentation/swift/dictionary/2894528-subscriptOptional なのに空文字列に変換したいという特殊なニーズだから仕方ないんじゃないかなぁ?
extension Optional where Wrapped : CustomStringConvertible { func description(default defaultValue: String) -> String { return self?.description ?? defaultValue } } (edited)String(describing: )でいいんじゃないかと思ったんですがこの引数の型なんだろ……print("\(String(describing: b))") // "Optional(くろ)\n" var c: Cat? = nil print("\(String(describing: c))") // "nil\n" (edited)OptionalはCustomDebugStringConvertibleですね CustomStringConvertibleではないです (ややこしや) でもこれってよく考えるとそうなっていてほしくて、文字列に変換できちゃうと困るけど、デバッグ時には文字列にしたいんですよね。extension Optional: CustomStringConvertible where Wrapped: CustomStringConvertible を自分で定義する、のはアリかもしれません。Optional が CustomStringConvertible だと "Optional(2018年)" の温床になるのでダメだと思います。.none のときに "" にしたいって話だからまた違う。extension Optional : CustomStringConvertible where Wrapped : CustomStringConvertible はまずそう。extension String { init<X>(describing: Optional<X>, default: String) } ↑こういう選択肢も・・・・infix operator ??? func ???<T>(lhs: Optional<T>, rhs: String) -> String { return lhs.map(String.init(describing:)) ?? rhs } let a: Int? = 0 print("\(a ??? "empty")") let b: Int? = nil print("\(b ??? "empty")") (edited)\( ) とは別に用意する?"\(foo)" である以上、それで CustomStringConvertible でないものを弾けないと "Optional(2018年)" を防ぎきれないと思うんですよねぇ。foo?.description ?? "" このように書くのは全く正しいけど、長いからこう書きたくないってことなので。 (edited)"\(a)" って書いちゃうケースを考えているんですね。 (edited)return "User(name=\(name), petCat=\(cat))"CustomStringConvertible でないものを文字列補間に突っ込んだら警告がいいと思うんですがどうでしょう? struct Foo {} let foo = Foo() print("\(foo)") // Foo() (edited)class Foo { init!() { return nil } }
init! 定義できるのに今更気づいたのですが、完全に使いどころがわからない。 (edited)convenience init から self.init() で呼べるっていうのだけど、それにしても (edited)init?() を self.init()! で呼べるし。func foo() -> Int! も一緒ですね。廃止して欲しい。CHANGELOG.mdに記述がありますね。 ### 2014-10-09 (Xcode 6.1) * Objective-C `init` and factory methods are now imported as failable initializers when they can return `nil`. In the absence of information about a potentially-`nil` result, an Objective-C `init` or factory method will be imported as `init!`. 今でもあるのかな?func f(_ value: Int?) {} @available(*, deprecated) func f(_ value: String?) {} f(nil) // error: ambiguous use of 'f'
deprecatedが付いてたら優先順位を変えてambiguous回避出来て欲しい。unavailable はどうでしょう?nil以外だとambiguousにならないので、残せるなら残したいのです。unavailable指定はambiguous回避出来ますが、書き換え強制になっちゃうんですよね。async な関数って inout で渡された引数にアクセスできるのは最初の await までだけになるのかな?inout で検索してもヒットしない・・・。 https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619func aaa(_ x: inout Int) -> () -> Void { func bbb() { x = 3 } return bbb } var t: Int = 3 aaa(&t)Swift:: Error: nested function cannot capture inout parameter and escape return bbb ^ Swift:: Error: expression resolves to an unused function aaa(&t) ^~~~~~~ await までは使えてほしいなぁ。@escaping でもできるっぽい。当然 @escaping しようが同期的な範囲でしか使えないけど。 func update<T>(_ value: inout T, _ operation: @escaping (inout T) -> ()) { operation(&value) }SwiftSourceKitClientとか出来るのか。* Implementation: [apple/swift#13361](https://github.com/apple/swift/pull/13361)
https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md
After reflecting on the evolution process during both the Swift 3 and Swift 4 releases, the Core Team felt that we could strike a balance with not diluting attention from ABI stability while still enabling a broader range of proposals compared to Swift 4 by **requiring that all proposals have an implementation** before they are officially reviewed by the Core Team.
https://forums.swift.org/t/swift-5-start-your-engines/6456break: () -> Never 作れそう。async/await 相当のことできないと辛くなって、 Promise 改造して↓を作ったけど、 https://github.com/koher/AsyncKasync/await in SwiftasyncFor を作って、 break を () -> Never にできないか考えたけどダメだった。 func asyncFor<S: Sequence>(_ sequence: S, _ operation: @escaping (S.Element, _ break: @escaping () -> ()) -> Async<Void>) -> Async<Void>
https://github.com/koher/AsyncK/blob/master/Sources/AsyncK/Async.swift#L80async/await in SwiftAProtocol: class にしたらエラーなくなる。 protocol AProtocol { var value: Int { get set } } struct B<T: AProtocol> { private let t: T init(_ t: T) { self.t = t } func get() -> T { return t } } class A: AProtocol { var value: Int init(value: Int) { self.value = value } } func foo<T: AProtocol>(a: T) { let b = B<T>(a) b.get().value += 1 // ここでコンパイルエラー print(b.get().value) } foo(a: A(value: 2))var b が必要、 : class が付くなら let b で OK 、になるかなclass 制約は AnyObject と同意になり、 class は deprecated です。 https://github.com/apple/swift-evolution/blob/master/proposals/0156-subclass-existentials.md#class-and-anyobject: AnyObject でまったく同じ意味になるので、機能が同じものを残しておく必要は無いということですね。To reduce source-breakage to a minimum, class could be redefined as typealias class = AnyObject and give a deprecation warning on class for the first version of Swift this proposal is implemented in. : class してた気がする。AnyObject と言えば、 Swift の関数(クロージャ)は参照型だけど === が AnyObject に対してしか使えなくて関数の同一性の比較ができないのどうにかならないのかな?protocol と interface 作って、 protocol は値型、 interface は参照型と分けてしまってもいいんじゃないかという気がする。protocol MyProtocol: AnyObject { } class Foo: MyProtocol { } func foo<X>(_ v: X) where X: MyProtocol {} func bar<X>(_ v: X) where X: AnyObject {} let a: MyProtocol = Foo() let b: AnyObject = Foo() // foo(a) // 🙅 // bar(a) // 🙅 bar(b) // 🙆Any も同じ?b: AnyAnyObject って感じだよね@objc が入ると更に大変なことになる// foo(a) // 🙅 // bar(a) // 🙅 もうこのまま突き進んでこれが通る方に倒したいprotocol の existential ですでにそうなのが・・・import Foundation @objc protocol MyProtocol: AnyObject { } class Foo: NSObject, MyProtocol { } func foo<X>(_ v: X) where X: MyProtocol {} func bar<X>(_ v: X) where X: AnyObject {} let a: MyProtocol = Foo() let b: AnyObject = Foo() foo(a) // 🙆 bar(a) // 🙆 bar(b) // 🙆@objc protocolは最早何でもあり (edited)@objc protocolに限りなく近い (edited)@objc が付いた protocol は型として使うとExistentialじゃなくなる?w%10 = open_existential_ref %9 : $Animal to $@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal // users: %12, %12, %11 %11 = witness_method [volatile] $@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal, #Animal.bark!1.foreign : <Self where Self : Animal> (Self) -> () -> String, %10 : $@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal : $@convention(objc_method) <τ_0_0 where τ_0_0 : Animal> (τ_0_0) -> @autoreleased NSString // type-defs: %10; user: %12 %12 = apply %11<@opened("00DAD0B2-1796-11E8-B1A7-8C85902C8E53") Animal>(%10) : $@convention(objc_method) <τ_0_0 where τ_0_0 : Animal> (τ_0_0) -> @autoreleased NSString // type-defs: %10; user: %14@objc protocolclass ReadWrite { } class ReadOnly: ReadWrite { } protocol IO { associatedtype Manifest: ReadWrite = ReadOnly } func foo<P>(_ arg: P) where P: IO, P.Manifest: ReadWrite { // ReadWrite }class ReadOnly { } class ReadWrite: ReadOnly { } protocol IO { associatedtype Manifest: ReadOnly = ReadOnly } func foo<P>(_ arg: P) where P: IO, P.Manifest: ReadWrite { // ReadWrite } class UserDefaults: IO { } foo(UserDefaults()) // 🙅 (edited)Optional では無いって条件みたいだけど適用できる?var foo = ImmutableFoo() foo = foo.update(...)var foo = ImmutableFoo() foo.update(...) でできて代入が不要になります。 immutable なのに mutating なのは名前がややこしいですが、 immutability は失われてないです。mutating func を使えても、 self 自体の置き換えはできてもインスタンスの状態は変更できないのでインスタンスの immutability は保たれます。 (edited)class 自身が自分のオブジェクトを書き換えるのはどういうシチュエーションなのかが気になるんですね self 置き換えがあまりないのかな?Promise とかそれで class にしてます。self 書き換えというのがいまひとつどれを書き換えているのか。self 置き換え可能と、プロパティを書き換え可能は独立した機能な気がしてる。class にとっては暗黙的に保証されてるけど、それとは別に var に格納されているときに self を書き換えられるという機能があり得るような。var と let の両方から参照されていることもあるから。mutating func というか inout self をしたい。func ( nonmutating func の省略表記 ) と mutating func がありますが、 cla...
protocol CharacterProtocol { init(name: String, hp: Int) var name: String { get } var hp: Int { get } mutating func update(hp: Int) } extension CharacterProtocol { mutating func update(hp: Int) { self = Self.init(name: name, hp: hp) } } class Character: CharacterProtocol { let name: String let hp: Int required init(name: String, hp: Int) { self.name = name self.hp = hp } } var character = Character(name: "ゆうしゃ", hp: 153) character.update(hp: character.hp - 10)//: Playground - noun: a place where people can play import UIKit protocol SelfRewritable { var id: Int { get set } init(id: Int) mutating func increaseID() } extension SelfRewritable { mutating func increaseID() { self = Self(id: self.id + 1) } } class User: SelfRewritable { var id: Int required init(id: Int) { self.id = id } } var user = User(id: 0) user.increaseID() user.idvar foo = Foo() var bar = foo bar.update(...) ってやったら、 foo と bar は別のものを指すようになるってことですよね。update がmutating funcかどうかがパッと見た目にわからないから、ぼくはそれは嫌かなー。protocol に : valueType が欲しいかな、mutating func 入れたい場合はextension Array { mutating func update(_ operation: (inout Element) -> ()) }Optional にも同じものを追加することはできますが、class で実装された Promise は同じような API を持っているのに update を追加できません。mutating func に inout self が混ざってしまっているのは、異なるものを一緒くたにしてしまっている気がします。struct ラップはあり得るけど、 Array の場合はミュータブルだからラップが必要だけど、 Promise の場合イミュータブル的に振る舞うから、本来不要なラッパーができるのが微妙かなぁ。self 置き換えはまた別じゃない?mutating func で self 置き換えができないようにして、それとは別に self 置き換えがあって区別できるのがいいかなと。そしたら、前者は var な値型のみ、後者は var ならクラスでも使えるようになる。mutating func が構文のことではなく(今の Swift の class のインスタンスメソッドと同じように) Stored Property を変更可能なメソッドのことならそうです。update func という構文を踏襲するなら。 mutating func は値型にのみ使用可能で Stored Property を更新可能、 self 置き換えは不可能、変数のような可変な lvalue に対してのみ呼び出し可能update func は値型にも参照型にも使用可能で self 置き換えが可能、変数のような可変な lvalue に対してのみ呼び出し可能protocol の mutating func が class でどうなるかは考え中。Range とか)ですと update func と mutating func の区別は生じる気がしないでもない Range の lowerBound と upperBound は let 宣言なのでmutating func を呼び出しても let プロパティは変更されないことが保証できるようになるね。struct Character { let name: String var hp: Int mutating func foo() { self = Character(name: "Hoge", hp: hp) } } var character = Character(name: "ゆうしゃ", hp: 153) character.foo() print(character.name) // "Hoge" (edited)Character はそもそも値型ではなく参照型で作るべきなのでは?と思わなくもないですねCharacter が代入されるたびにコピーされてしまうのでwstruct でやるのが僕の try! Swift のワークショップなのですupdate は単語が微妙だったので replacing にしてみました。 struct Character { let name: String var hp: Int mutating func a() { self = Character(name: "Hoge", hp: hp) // NG } replacing func b() { self = Character(name: "Hoge", hp: hp) // OK } mutating func c() { self = Character(name: name, hp: 42) // NG hp = 42 // OK } replacing func d() { self = Character(name: name, hp: 42) // OK } } (edited)mutating replacing func もありです。var character: Character の方の宣言ですね…character が a() と c() が呼べて b() と d() が呼べない、もしくはその逆の保証をするのかですね…var character だと mutating も replacing も呼べる想定ですね。 (edited)mutating と replacing の区別をする意味がなくなるのでは?というところですmutating func をコールした場合に let プロパティが書き換えられてしまうことがないことを保証できます。replacing func は class でも使えます。replacing は参照型限定にした方が実装の意味がある気がします replacing 導入しても、結局 var だとその実装が呼べる呼べない区別ができなくなってしまいますのでlet プロパティが書き換えられてるのって直感に反しませんか? struct Character { let name: String var hp: Int mutating func foo() { self = Character(name: "Hoge", hp: hp) } } var character = Character(name: "ゆうしゃ", hp: 153) character.foo() print(character.name) // "Hoge"replacing func を呼ぶ場合は &character.foo() とか特殊な構文にしてもいいかも?var 宣言使うと replacing も mutating も使えるし、逆に let 宣言使うと replacing も mutating も使えなくなって、区別ができなくなってしまうのが気になるポイントですreplacing func の呼び出しは inout のように変化を付けたいですね。let と var 以外の第3の宣言が必要になってくると言語仕様がどんどん複雑に… inout character: Character 的な宣言です?inout let か inout var にしないとですね &character.foo() 的な。var じゃないと &character 使えないですね (edited)replacing func は使えないようにしたいけど mutating func は使いたいというニーズはほぼないと思うんですよね。extension Int { mutating func double() { self = self * 2 } } var i = 3 &i.double() こう? (edited)inout は mutating と等価だから、 & で被るのは微妙かも。let 宣言使う以外 &i を防ぐ手段がない、というのば私が言いたいことですねreplacing できないけど mutating できる第三の変数みたいなのもあり得るけど、それの意味するところは再代入できるけどプロパティの更新はできる変数と定数の中間のものを作りたいということになるから (edited)replacing func とは直交する話な気がします。mutating 可と replacing 不可を実現するのであって、概念としては直交してる気がします。)i.foo() と書くか、&i.foo() とかくかの区別だけしかできない、というのが私の懸念点ですねreplacing を参照型だけのものにするか、もしくは値型にも replacing を導入するのあら、何らかの宣言レベルでの区別が欲しいというのが私の考えですlet プロパティを更新する挙動をするので利用者を驚かせてしまうと思うんですが、 var character = Character(name: "ゆうしゃ", hp: 153) character.foo() print(character.name) // "Hoge" ↓これだと驚かないと思うんですよね。 var character = Character(name: "ゆうしゃ", hp: 153) character = Character(name: "Hoge", hp: character.hp) print(character.name) // "Hoge"var 自体ができることをどうこうしたいというわけではなくてlet プロパティが書き換えられてしまうことを防ぎたいということです。&character.foo() のようにコール時に明示的な区別がされれば OK だと思います。inout ですでに行われていて、もし↓のコードが & なしでコールできたらユーザーを驚かせてしまうと思うんですね。 var a = 2, b = 3 swap(&a, &b) print(a) // 3 print(b) // 2var で宣言するか let で宣言するかで区別しているのでvar のプロパティが変更されることは当然あり得ると思うんですが、 let のプロパティが変更されるのは直感に反しませんか?struct のプロパティで) var か let かを区別する機能を殺してると思うんですね。mutating func と replacing func を分ければ「たとえletであっても、ユーザー側でextensionを使ってsetterを生やせる。」も同じものは提供できなくなります。 replacing func のコールで & 必須だと。i.foo() より &i.foo() の方がわかりやすい、というのはわかりますが、そこまでやるならやはり inout var i 宣言が欲しいかな、と思いますi += 1 // i = i + 1 だから、 i.=foo() // i = i.foo() とか(いや、やりたいことと違う)。->だったら (edited)i=>foo() // i = i -> foo() とかワンチャンあったかも(ない)protocol P1 { init() } protocol P2 { init() } struct S { func a<T: P1>(_ type: T.Type) -> T { return a(type) ?? type.init() } private func a<T>(_ type: T.Type) -> T? { guard let p2Type = type as? P2.Type else { return nil } return p2Type.init() as? T } } extension Int: P1 {} S().a(Int.self) // Swift 4.1より前は無限ループOptional<T> 期待になるから private 側が呼び出されてるってことっぽい。 (inject_into_optional implicit type='T?' location=a.swift:12:16 range=[a.swift:12:16 - line:12:22] (call_expr type='T' location=a.swift:12:16 range=[a.swift:12:16 - line:12:22] nothrow arg_labels=_:a(type) のところの call_exprがTで、その一個上に inject_into_optional があるからそうっぽいT? になっているのかな。[omochi@omochi-MB2 infer]$ export TOOLCHAINS=org.swift.3020180227a [omochi@omochi-MB2 infer]$ swift --version Apple Swift version 4.1-dev (LLVM c4ec2ab808, Clang af436f313e, Swift 5f2f440067)swift-4.1-DEVELOPMENT-SNAPSHOT-2018-02-28-aのREPLも落ちないです。 (edited)swift-DEVELOPMENT-SNAPSHOT-2018-02-27-aですね。masterのスナップショット。swift-4.1-branchのスナップショットならorg.swift.3020180227aではなくorg.swift.4120180227aなはず。org.swift.4120180228a だと実行できた。(call_expr type='T?' location=a.swift:12:16 range=[a.swift:12:16 - line:12:22] nothrow arg_labels=_:T? で推論されて、暗黙キャストが無くなった (edited)Data の↓の引数が UnsafeRawBufferPointer じゃないのってなんでなんですっけ? init(bytes: UnsafeRawPointer, count: Int)
https://developer.apple.com/documentation/foundation/data/1780158-init (edited)NSData とは別に Data を作ったわけで、 RawBuffer でもよかった気がするんですがいかがでしょう?Data の導入とポインタ系の整理のタイミング問題でそうなってしまったとか?UnsafeRawBufferPointer 版が無いのは UnsafeRawBufferPointer が出てきたときに Data 側対応が漏れているだけだと思います。Buffer 版を追加しようという提案はあり得そうですね。 https://developer.apple.com/documentation/foundation/data/1780455-init[array] が再現のポイントです。それがなければ通ります。 func foo<R, T>( array: [T], operation: (Int) throws -> R ) rethrows -> R { return try array.withUnsafeBytes { [array] _ in return try operation(array.count) } }
error: repl.swift:5:16: error: call can throw, but the error is not handled; a function declared 'rethrows' may only throw if its parameter does return try array.withUnsafeBytes { [array] _ in ^ repl.swift:5:38: note: call is to 'rethrows' function, but argument function can throw return try array.withUnsafeBytes { [array] _ in ^ (edited)error: overlapping accesses to 'array', but modification requires exclusive access; consider copying to a local variable です。
(edited)call can throw, ...)はバグっぽい。 (edited)[array] なくせば通りました。
defer { _precondition( inoutBufferPointer.baseAddress == pointer && inoutBufferPointer.count == count, "${Self} withUnsafeMutableBufferPointer: replacing the buffer is not allowed") (work, self) = (self, work) }inout の out を除いたもの」として、 @escaping なクロージャにキャプチャされるのを防ぐために使われてるのかな?withUnsafeBufferPointer がそうなってないのが謎だけど。withUnsafe*Pointer系は全部そうなってても良い気がする。withUnsafeMutableBufferPointer だけそうなのがよくわからない。inout の out なし版( @escaping に渡すのを防ぐだけ)がほしい。import UIKit protocol SomeChild where Self : UIViewController { func doSomething() } extension SomeChild { func doSomething() { print(self.title) } } class ChildViewController: UIViewController, SomeChild {} class ParentViewController: UIViewController { var children: [SomeChild] = [ChildViewController()] var child: SomeChild = ChildViewController() } let parent = ParentViewController() parent.child.doSomething() parent.children.forEach { $0.doSomething() } childにアクセスすると問答無用でEXC_BAD_ACCESSになるprotocol SomeChild { func doSomething() } extension SomeChild where Self : UIViewController { func doSomething() { print(self.title) } }class Object {} protocol SomeChild where Self : Object { func doSomething() } extension SomeChild { func doSomething() { print(self) } } class ChildObject: Object, SomeChild {} class ParentObject: Object { var children: [SomeChild] = [ChildObject()] var child: SomeChild = ChildObject() } let parent = ParentObject() parent.child.doSomething() parent.children.forEach { $0.doSomething() }import UIKit protocol SomeChild where Self : UIViewController { func doSomething() init() } extension SomeChild { func doSomething() { print(self.title as Any) } } class ChildViewController: UIViewController, SomeChild {} class ParentViewController<C: SomeChild>: UIViewController { var children: [C] = [C()] var child: C = C() } let parent = ParentViewController<ChildViewController>() parent.child.doSomething() parent.children.forEach { $0.doSomething() }class Object {} protocol SomeChild where Self : Object { func doSomething() } class ChildObject: Object, SomeChild { func doSomething() { print("hello") } } class ParentObject: Object { var children: [SomeChild] = [ChildObject()] var child: SomeChild = ChildObject() } let parent = ParentObject() parent.child.doSomething() parent.children.forEach { $0.doSomething() }class Object {} protocol SomeChild where Self : Object { func doSomething() } class ChildObject: Object, SomeChild { func doSomething() { print("hello") } } let c: SomeChild = ChildObject() c.doSomething() // 😇class Object { var x: Int = 1 } protocol SomeChild where Self : Object { func doSomething() } class ChildObject: Object, SomeChild { func doSomething() { print("hello") } } let c: SomeChild = ChildObject() c.x // Compile Error c.doSomething() SomeChildはObjectを満たしているはずだが、xを呼び出せないlet c: SomeChild & Object = ChildObject() って書くと動く。protocol SomeChild where Self : AnyObject { func doSomething() } class ChildObject: SomeChild { func doSomething() { print("hello") } } let c: SomeChild = ChildObject() c.doSomething() 最小これか。まあこれは protocol SomeChild: AnyObject { って書けば良いじゃんて話ですが。Self:AnyObjectと:AnyObjectで動き変わるんすねTYPE MISMATCH IN ARGUMENT 0 OF APPLY AT expression at [<REPL>:12:1 - line:12:15] RangeText="c.doSomething()" argument value: %13 = open_existential_addr immutable_access %6 : $*SomeChild to $*@opened("25C7E87E-2911-11E8-A83E-0242AC110002") SomeChild // user: %14 parameter type: $@opened("25C7E87E-2911-11E8-A83E-0242AC110002") SomeChildprotocol … where Self: …っていう書き方、最近まで知らなかった。Optional: Hashable where Wrapped: Hashable になってないみたいですがなぜでしょう?Equatable はもう実装されてるようなので、もし Hashable も同じようにするならもう実装済みだろうと思いこんでました。func makeFunc<A, R>(argType: A.Type, returnType: R.Type, body: @escaping (A) -> R) -> (A) -> R { return body } makeFunc(argType: Int.self, returnType: String.self) { String($0) }(3) makeFunc(argType: (Int, Int).self, returnType: (String, String).self) { (String($0.0), String($0.1)) }((1, 2)) makeFunc(argType: Void.self, returnType: Void.self) { _ -> Void in print(33) }(()) // error makeFunc(argType: ().self, returnType: Void.self) { _ -> Void in print(33) }(()) // error makeFunc(argType: Void.self, returnType: ().self) { _ -> Void in print(33) }(()) print( ().self is Void.Type ) // false print( Void.self is ().Type ) // true() と Void は違うものだった?print(().self is ()) // trueprint(().self == ()) // true 何だこれは// error だけど 引数のところは許された・・・ makeFunc(argType: (), returnType: ()) { _ in print(33) }(())() を型としては扱えないようです。let a = [()]() // error() と 型の () があるのか()が型である必要って今何かあるんですっけ? 前は()->Voidで必要でしたけど (edited)(Int) -> () 型を禁止するわけにはいかないからでしょうか。 // Fold 'P & Q' into a composition type if (auto *binaryExpr = dyn_cast<BinaryExpr>(E)) {Voidでいいと言うかもともと()->()みたいな書き方しなかったですね……@objcな関数や変数をoverrideすると、overrideしたそれも@objcの扱いになっているっぽくて@objcではネストしたOptionalは許可されないので、コンパイルエラーになる()型 の typealias であって、()値 と ()型 は違います。let hoge: () って書けましたっけ?(1, 2)で、型の表記が(Int, Int)なのだが、これが空になるとどちらも()になるので混乱するlet void: Void = ()って書いておいて、型ならVoid、値ならvoidを使うようにすれば楽になりそうだと一日考えて思った。let void = () func xxx() -> Void { return void } こういうことです? グローバルにあるのも違和感なので extension Void { static var void: Void = () } func xxx() -> Void { return .void } こんなかんじですかね(extensionかけないけど)let void: Void = () func nop<T>(_ arg: T) { } func undefined<T>() -> T { preconditionFailure() } この辺大好き (edited)protocol Foo { } struct FooStruct: Foo { } protocol FooProtocol: Foo { } protocol Bar { func accept(_ foo: FooStruct) func accept<X>(_ foo: X) where X: FooProtocol } class BarImpl: Bar { func accept<X>(_ foo: X) where X: Foo { } }protocol Root {} protocol Foo : Root {} protocol Bar : Root {} protocol A { func accept<F : Foo>(_ foo: F) func accept<B : Bar>(_ bar: B) } extension A { func accept<F : Foo>(_ foo: F) { print("Foo: \(foo)") } func accept<B : Bar>(_ bar: B) { print("Bar: \(bar)") } } class B : A { func accept<R : Root>(_ root: R) { print("Root: \(root)") } } struct FooImpl : Foo {} struct BarImpl : Bar {} B().accept(FooImpl()) B().accept(BarImpl()) (edited)protocol Root {} protocol Foo : Root {} protocol Bar : Root {} class A { func accept<F : Foo>(_ foo: F) { print("Foo: \(foo)") } func accept<B : Bar>(_ bar: B) { print("Bar: \(bar)") } } class B : A { override func accept<R : Root>(_ root: R) { print("Root: \(root)") } } struct FooImpl : Foo {} struct BarImpl : Bar {} B().accept(FooImpl()) B().accept(BarImpl())protocol Root {} protocol Foo : Root {} protocol Bar : Root {} protocol A { func accept(_ foo: Foo) func accept(_ bar: Bar) } extension A { func accept(_ foo: Foo) { print("Foo: \(foo)") } func accept(_ bar: Bar) { print("Bar: \(bar)") } } class B : A { func accept(_ root: Root) { print("Root: \(root)") } } struct FooImpl : Foo {} struct BarImpl : Bar {} B().accept(FooImpl()) // Root: FooImpl() B().accept(BarImpl()) // Root: BarImpl() (edited)let a: A = B() a.accept(FooImpl()) // Foo: FooImpl() a.accept(BarImpl()) // Bar: BarImpl()accept がジェネリックだったらちゃんとオーバーライドできてた。protocol Root {} protocol Foo : Root {} // 1 class A { func accept<R : Root>(_ root: R) { } } class B : A { override func accept<R : Foo>(_ root: R) { } } // 2 class A2 { func accept<R : Foo>(_ root: R) { } } class B2: A2 { override func accept<R : Root>(_ root: R) { } }protocol Root {} protocol Foo : Root {} // 1 class A { func accept<R : Root>(_ root: R) { } } class B : A { override func accept<R : Foo>(_ root: R) { } } // 2 class A2 { func accept<R : Foo>(_ root: R) { } } class B2: A2 { override func accept<R : Root>(_ root: R) { } } struct RootImpl : Root {} struct FooImpl : Foo {} let b: B = B() // b.accept(RootImpl()) // コンパイルエラー let a: A = b a.accept(RootImpl()) // OK (edited)protocol Root {} protocol Foo : Root { func foo() } // 1 class A { func accept<R : Root>(_ root: R) { } } class B : A { override func accept<R : Foo>(_ root: R) { root.foo() } } // 2 class A2 { func accept<R : Foo>(_ root: R) { } } class B2: A2 { override func accept<R : Root>(_ root: R) { } } struct RootImpl : Root {} struct FooImpl : Foo { func foo() { print("foo") } } let b: B = B() // b.accept(RootImpl()) // コンパイルエラー let a: A = b a.accept(RootImpl()) // 実行時エラーmaster からビルドした最新の swift でもダメだった。。。 (edited)protocol AnimalProtocol { func speak() -> String } protocol CatProtocol : AnimalProtocol { func nya() -> String } class AnimalEater { func accept<X : AnimalProtocol>(_ x: X) {} } class CatEater : AnimalEater { override func accept<X : CatProtocol>(_ x: X) { print(x.nya()) } } class Dog : AnimalProtocol { func speak() -> String { return "bow wow" } } let catEater = CatEater() let animalEater = catEater as AnimalEater let dog = Dog() animalEater.accept(dog) // file:///Users/omochi/work/playground/iOSGround.playground: error: Playground execution aborted: error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x0). // The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation. (edited)StringProtocol.hasPrefix(_:)が似た状況かも。 https://github.com/apple/swift/commit/f9b3e14137ac2f50e93aa2c1db28511e05f88e75public protocol StringProtocol … { … func hasPrefix(_ prefix: String) -> Bool で、実装が extension StringProtocol { … public func hasPrefix<Prefix: StringProtocol>(_ prefix: Prefix) -> Bool { になってる。 (edited)Stringはジェネリックになってない。 extension String { … public func hasPrefix(_ prefix: String) -> Bool {protocol ValuePrinter { func print(_ x: Int) func print(_ x: String) } protocol IntOrString { func asInt() -> Int? func asString() -> String? } extension Int : IntOrString { func asInt() -> Int? { return self } func asString() -> String? { return nil } } extension String : IntOrString { func asInt() -> Int? { return nil } func asString() -> String? { return self } } class OmniValuePrinter : ValuePrinter { // oneshot double override! func print<X: IntOrString>(_ x: X) { if let i = x.asInt() { Swift.print(i) } if let s = x.asString() { Swift.print(s) } } } let printer = OmniValuePrinter() as ValuePrinter printer.print(3) printer.print("a")decl/** に構文単位でいっぱいあるのか。$ nm /Users/norio/Library/Developer/Xcode/DerivedData/ObjectEncoder-cvflgtjlegddrlbosxxjagoaqewr/Build/Products/Debug/ObjectEncoder.framework/ObjectEncoder|xcrun swift-demangle … 00000000000305e0 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.String.Type) throws -> Swift.String in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder 000000000002f000 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.Bool.Type) throws -> Swift.Bool in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder 0000000000030430 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.Double.Type) throws -> Swift.Double in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder 0000000000030280 t _protocol witness for Swift.SingleValueDecodingContainer.decode(Swift.Float.Type) throws -> Swift.Float in conformance ObjectEncoder.ObjectDecoder.Decoder : Swift.SingleValueDecodingContainer in ObjectEncoder …class A: AProtocol { init() {} } fileprivate protocol AProtocol { var value: String { get } } extension AProtocol { var value: String { get { return "" } } }struct B { fileprivate static var a = A() static var v: String { get { return a.value } } }error: 'value' is inaccessible due to 'fileprivate' protection level が正解かと。$ swiftc A.swift B.swift -emit-library -module-name MyMod 通る $ swiftc A.swift B.swift -emit-library -module-name MyMod -wmo (edited)public func neverHappen(file: StaticString = #file, line: UInt = #line) -> Never { fatalError("never happen", file: file, line: line) } public func neverHappen<T0>(_ arg0: T0, file: StaticString = #file, line: UInt = #line) -> Never { fatalError("never happen: arg0=\(arg0)") } 例えばこうやって使う Observable.just(()).asDriver(onErrorRecover: { neverHappen($0) }) (edited)asDriver(onErrorRecover: neverHappen) (edited)asDriver(onErrorRecover: neverHappen()) こういう事?asDriver(onErrorRecover: { _ in neverHappen() }) ってかいてたけど_ in で捨てているのはもったいないから() ついてるの何ってなるでしょfunc a(x: Int, y: Int = 3) is (Int) -> Void#fileとか)があるので、結局コンパイラがクロージャ的なコードを生成する必要がある。 (edited)xOrNone.map(cat.speak) とかやるときも、 cat を束縛するクロージャができてますよね? // In the defining module. func foo_impl(a: Int, b: Int, c: Float) {...} func foo_b() -> Int { return 42 } func foo_c() -> Float { return 3.14 } // On the caller side. let bval = foo_b() let cval = foo_c() foo_impl(a: 192, b: bval, c: cval) ↑そうそうコレ // In the defining module. func foo_impl(a: Int, b bval: Int?, c val: Float?) { let b = bval ?? 42 let c = cval ?? 3.14 ... } // On the caller side. foo_impl(a: 192, b: nil, c: nil)Hashable の conformance とかまだ入ってないですね。 (edited)Double の Vector3 とか Vector4 を作ってたけど、仕事で Float にも対応したものがほしくなって、 ARKit の vector_float3 互換にもしたかったので、 gyb で色々生成してみた。(ついでにライブラリ名を SwiftyVector にリネーム) https://github.com/koher/SwiftyVector/blob/0.3.0/Sources/SwiftyVector/Concrete.swift.gyb (edited)protocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P typealias Bar = [Piyo] // Require } これ結構気になってるprotocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P // typealias Bar = [Piyo] // Require }<unknown>:0: error: unknown argument: '--version=latest'Swift version 4.2-dev (LLVM d14a2b25f2, Clang c38020c511, Swift 22530b922f)/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^protocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P // typealias Bar = [Piyo] // Require }Swift version 4.1 (swift-4.1-RELEASE)/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^protocol Foo { associatedtype Bar } protocol FooA: Foo where Bar == String { } struct FooAImpl: FooA { // typealias Bar == String // Not require } protocol FooB: Foo where Bar == [Piyo] { associatedtype Piyo } struct FooBImpl<P>: FooB { typealias Piyo = P // typealias Bar = [Piyo] // Require }Swift version 4.2-dev (LLVM d14a2b25f2, Clang c38020c511, Swift 22530b922f)/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^Swift version 4.1 (swift-4.1-RELEASE)/usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'Foo' struct FooBImpl<P>: FooB { ^ /usercode/main.swift:2:20: note: protocol requires nested type 'Bar'; do you want to add it? associatedtype Bar ^ /usercode/main.swift:17:8: error: type 'FooBImpl<P>' does not conform to protocol 'FooB' struct FooBImpl<P>: FooB { ^typealias Bar = [Piyo] が省略できないのは、 typealias Piyo = P が無いと推論できない事だから?typealias Bar == String が省略できるのは、 : FooA だけで推論できることだから? a = someValue[dynamicMember: "someMember"] someValue[dynamicMember: "someMember"] = a mutateParameter(&someValue[dynamicMember: "someMember"]) みたいなのは現状でもやろうと思えばできて、 a = someValue.someMember someValue.someMember = a mutateParameter(&someValue.someMember) のように書けるようにするだけだと思う。$ ls Python.framework/Versions/ 2.3 2.5 2.6 2.7 Current (edited)/System/Library/Frameworks/Python.framework は触らずに別 Framework にした方が良さそうに思います。map が Python 2 系では List を返していたのが 3 では iterable なオブジェクトになってるとか、吸収しきれなさそう。/Library/Frameworks にインストールされました。/System/Library/Frameworks 以下はそのままのようですね。 $ ls /Library/Frameworks/Python.framework/Versions 3.6 $ ls /System/Library/Frameworks/Python.framework/Versions 2.3 2.5 2.6 2.7 Currentpyenvの場合 $ PYTHON_CONFIGURE_OPTS="--enable-framework" pyenv install 3.6.4 python-build: use openssl from homebrew python-build: use readline from homebrew Downloading Python-3.6.4.tar.xz... -> https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tar.xz Installing Python-3.6.4... python-build: use readline from homebrew Installed Python-3.6.4 to /Users/norio/.pyenv/versions/3.6.4 208.44s user 58.92s system 162% cpu 2:44.55 total $ swift `python-config --ldflags` -I `python-config --prefix` Welcome to Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.39.1). Type :help for assistance. 1> import Python 2> func execPy(_ code: String) { 3. Py_Initialize() 4. PyRun_SimpleStringFlags(code, nil) 5. Py_Finalize() 6. } 7> execPy("import sys\nprint(sys.version)") 3.6.4 (default, Apr 24 2018, 13:35:20) [GCC 4.2.1 Compatible Apple LLVM 9.1.0 (clang-902.0.39.1)] 8> (edited)ArraySlice で任意の範囲のインデックスを持つインスタンスを生成する方法ってありませんか?たとえば↓みたいな感じで。 // こんなことがしたい let slice: ArraySlice<String> = ["a", "b", "c"].offset(100) print(slice[100]) // "a" print(slice[101]) // "b" print(slice[102]) // "c" (edited)ArraySlice をそういう目的で使うのが間違いな気がしてきました。負のインデックスもできないみたいだし。startIndex の前に insert することもできない。struct A { var strings: AnySequence<String> { return AnySequence { () -> AnyIterator<String> in var buffer = staticStrings ?? [] return AnyIterator { if let first = buffer.popFirst() { return first } return nil } } } } let staticStrings: [String]? = ["1", "2"] (edited)main.swift:6:32: error: cannot use mutating member on immutable value: 'buffer' is immutable if let first = buffer.popFirst() { ^~~~~~ (edited)bufferはimmutableなの?main.swift:6:39: error: '[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalent to use 'popFirst' if let first = buffer.popFirst() { ^struct A { var strings: AnySequence<String> { return AnySequence { () -> AnyIterator<String> in var buf: [String] = ["1", "2"] return AnyIterator { if let first = buf.popFirst() { return first } return nil } } } }main.swift:6:36: error: '[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalent to use 'popFirst' if let first = buf.popFirst() { ^var buf: [String] = ["1", "2"] buf.popFirst()main.swift:2:5: error: '[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalent to use 'popFirst' buf.popFirst() ^Playground execution failed: error: iOSGround.playground:13:1: error: cannot use mutating member on immutable value: 'buf' is immutable buf.popFirst() ^~~extension Collection where SubSequence == Self { /// Removes and returns the first element of the collection. /// /// - Returns: The first element of the collection if the collection is /// not empty; otherwise, `nil`. /// /// - Complexity: O(1) @_inlineable public mutating func popFirst() -> Element? { // TODO: swift-3-indexing-model - review the following guard !isEmpty else { return nil } let element = first! self = self[index(after: startIndex)..<endIndex] return element } }'[String]' requires the types '[String]' and 'ArraySlice<String>' be equivalentstruct A { var strings: AnySequence<String> { return AnySequence { () -> AnyIterator<String> in var buffer = ArraySlice(staticStrings ?? []) return AnyIterator { if let first = buffer.popFirst() { return first } return nil } } } } let staticStrings: [String]? = ["1", "2"] (edited)var buf = ArraySlice([1,2,3]) print(buf) buf.popFirst() print(buf)[1, 2, 3] [2, 3]stderr:main.swift:3:5: warning: result of call to 'popFirst()' is unused buf.popFirst() ^ ~~var a = [2, 3, 5] print(a.popFirst()!) (edited)main.swift:2:7: error: cannot use mutating member on immutable value: 'a' is immutable print(a.popFirst()!) ^ (edited)ArraySlice ならちゃんと popFirst があるから使える。 @swift-4.1.3
var a: ArraySlice<Int> = [2, 3, 5] print(a.popFirst()!) (edited)error: cannot use mutating member on immutable value: 'a' is immutable なるメッセージが出ることになるのかは興味深いけど・・・。ArraySlice でやるべきと思ってたんだけど、 (edited)ArraySlice はインデックスを保存するからextension Collection { func indexed() -> [(Index, Element)] { return indices.map { ($0, self[$0]) } } } let a = [ 2, 3, 5, 7, 11, 13] var b = a[2...4] print(Array(b.indexed())) _ = b.popLast() b.append(11) print(Array(b.indexed())) _ = b.popFirst() b.insert(5, at: 3) print(Array(b.indexed()))Swift version 4.1 (swift-4.1-RELEASE)[(2, 5), (3, 7), (4, 11)] [(2, 5), (3, 7), (4, 11)] [(3, 5), (4, 7), (5, 11)][(2, 5), (3, 7), (4, 11)] を取り戻す方法がない気がしてます。extension Collection { func indexed() -> [(Index, Element)] { return indices.map { ($0, self[$0]) } } } let a = ["a", "b", "c", "d", "e", "f", "g"] var b = a[2...4] print(Array(b.indexed())) _ = b.popLast() b.append("e") print(Array(b.indexed())) _ = b.popFirst() b.insert("c", at: 3) print(Array(b.indexed())) (edited)Swift version 4.1 (swift-4.1-RELEASE) (edited)[(2, "c"), (3, "d"), (4, "e")] [(2, "c"), (3, "d"), (4, "e")] [(3, "c"), (4, "d"), (5, "e")]startIndex を変える方法がない気がしてる。endIndex は伸びるんだけど。startIndex の前に挿入できないと全部ずらすことになっていて O(N) になってるかも。appendFirst とか pushFirst とか insertFirst がない。func indexed<T>(_ slice: ArraySlice<T>) -> [(Int, T)] { return (0..<slice.count).map { (offset: Int) in (offset, slice[slice.startIndex + offset]) } } let a = ["a", "b", "c", "d", "e", "f", "g"] var b = a[2...4] print(indexed(b)) _ = b.popLast() b.append("h") print(indexed(b)) _ = b.popFirst() b.insert("i", at: 3) print(indexed(b))Swift version 4.1 (swift-4.1-RELEASE)[(0, "c"), (1, "d"), (2, "e")] [(0, "c"), (1, "d"), (2, "h")] [(0, "i"), (1, "d"), (2, "h")]ArraySlice の subscript の仕様を使うとおもしろいことができて、var cachedFrames = ArraySlice<Image<RGBA<UInt8>>> としてキャッシュしておけば、 (edited)cachedFrames[frameIndex] で簡単にアクセスできる。if cachedFrames.count > maxNumberOfCachedFrames { cachedFrames.removeFirst() } みたいにして簡単に古いキャッシュを消せるしif cachedFrames.indices.contains(frameIndex) { ... } みたいにキャッシュが存在するときの処理も簡単に書ける。(cachedFrames.startIndex ..< cachedFrames.endIndex).contains(frameIndex) にすれば O(1) ) (edited)Array とは別物になるしArraySlice として作ってる。class A { deinit { print("deinit") } } var x: ArraySlice<A> = [A(), A(), A()] print("A") x.removeFirst() print("B") x.removeFirst() print("C") x.removeFirst() print("D") (edited)Swift version 4.1 (swift-4.1-RELEASE) (edited)A B C Dclass A { deinit { print("deinit") } } var x: ArraySlice<A> = [A(), A(), A()] print("A") x.removeFirst() x[2] = A() print("B") x.removeFirst() x[2] = A() print("C") x.removeFirst() print("D")Swift version 4.1 (swift-4.1-RELEASE)A deinit B deinit C DremoveFirst されたやつらも解放されると思うんだけど、どうやって試すのがいいかな・・・。class A { deinit { print("deinit") } } var x = [A(), A(), A()] print("A") x.removeLast() print("B") x.removeLast() print("C") x.removeLast() print("D")Swift version 4.1 (swift-4.1-RELEASE)A deinit B deinit C deinit DremoveFirst じゃ ArraySlice はバッファの中身をクリアしないっぽいな。removeFirstされた要素もコピーされてそう。startIndex から endIndex の分だけコピーされてんじゃないかと思ってるんですが。class A { deinit { print("deinit") } } var x: ArraySlice<A> = [A(), A(), A()] do { let y = x print("A") x.removeFirst() x[2] = A() print("B") x.removeFirst() x[2] = A() print("C") x.removeFirst() print("D") } print("E")Swift version 4.1 (swift-4.1-RELEASE)A B deinit C D deinit deinit E/usercode/main.swift:7:9: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x ~~~~^ _class A : CustomStringConvertible{ let val: Int init(_ val: Int) { self.val = val } deinit { print("deinit: \(self)") } var description: String { return "A(\(val))" } } var x: ArraySlice<A> = [A(1), A(2), A(3)] do { let y = x print("A") x.removeFirst() x[2] = A(4) print("B") x.removeFirst() x[2] = A(5) print("C") x.removeFirst() print("D") } print("E")Swift version 4.1 (swift-4.1-RELEASE)A B deinit: A(4) C D deinit: A(1) deinit: A(3) E/usercode/main.swift:12:7: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x ~~~~^ _A(1) が deinit されるのか?deinit されてるの変だ。なんで 1 も解放されてるんだろう・・・。 (edited)removeFirstされた要素はCoWでコピーされてない様ですね。 x[2] = A(4) でCoW発生か。A(2) も解放されないといけない気が。ちょっと紙に書いてみないと頭の中で CoW 含む参照関係が追えなくなってきたので間違ってるかもですが・・・。A(2) もされるべきっぽいし、妙に中途半端。class A : CustomStringConvertible { let value: Int init(_ value: Int) { self.value = value } var description: String { return "A(\(value))" } deinit { print("deinit \(self)") } } do { var x: ArraySlice<A> = [] for i in 0..<20 { x.append(A(i)) } while let a = x.popFirst() { print("pop \(a)") } print("-----") }Swift version 4.1 (swift-4.1-RELEASE)pop A(0) pop A(1) pop A(2) pop A(3) pop A(4) pop A(5) pop A(6) pop A(7) pop A(8) pop A(9) pop A(10) pop A(11) pop A(12) pop A(13) pop A(14) pop A(15) pop A(16) pop A(17) pop A(18) pop A(19) ----- deinit A(0) deinit A(1) deinit A(2) deinit A(3) deinit A(4) deinit A(5) deinit A(6) deinit A(7) deinit A(8) deinit A(9) deinit A(10) deinit A(11) deinit A(12) deinit A(13) deinit A(14) deinit A(15) deinit A(16) deinit A(17) deinit A(18) deinit A(19)class A : CustomStringConvertible { let value: Int init(_ value: Int) { self.value = value } var description: String { return "A(\(value))" } deinit { print("deinit \(self)") } } var i = 0 do { var x: ArraySlice<A> = [] do { for _ in 0..<20 { x.append(A(i)) i += 1 } while let a = x.popFirst() { print("pop \(a)") if !x.isEmpty { x[x.endIndex - 1] = A(i) i += 1 } } } print("-----") }Swift version 4.1 (swift-4.1-RELEASE)pop A(0) deinit A(19) pop A(1) deinit A(20) pop A(2) deinit A(21) pop A(3) deinit A(22) pop A(4) deinit A(23) pop A(5) deinit A(24) pop A(6) deinit A(25) pop A(7) deinit A(26) pop A(8) deinit A(27) pop A(9) deinit A(28) pop A(10) deinit A(29) pop A(11) deinit A(30) pop A(12) deinit A(31) pop A(13) deinit A(32) pop A(14) deinit A(33) pop A(15) deinit A(34) pop A(16) deinit A(35) pop A(17) deinit A(36) pop A(18) deinit A(37) pop A(38) ----- deinit A(0) deinit A(1) deinit A(2) deinit A(3) deinit A(4) deinit A(5) deinit A(6) deinit A(7) deinit A(8) deinit A(9) deinit A(10) deinit A(11) deinit A(12) deinit A(13) deinit A(14) deinit A(15) deinit A(16) deinit A(17) deinit A(18) deinit A(38)while let ちゃんと使ったの初めてかも。A(2)もコピーされてるから、解放されなくて正解な気が。 @swift-4.1.3
class A : CustomStringConvertible{ let val: Int init(_ val: Int) { self.val = val } deinit { print("deinit: \(self)") } var description: String { return "A(\(val))" } } var x: ArraySlice<A> = [A(1), A(2), A(3)] do { let y = x // yは[A(1), A(2), A(3)]を保持 print("A") x.removeFirst() // xは[A(1), A(2), A(3)]を保持して[A(2), A(3)]を露出 x[2] = A(4) // CoW発生、xは[A(2), A(4)]を保持 print("B") x.removeFirst() // xは[A(2), A(4)]を保持して[A(4)]を露出 x[2] = A(5) // xは[A(2), A(5)]を保持して[A(5)]を露出、A(4)解放 print("C") x.removeFirst() // xは[A(2), A(5)]を保持して[]を露出 print("D") } // y解放、xに保持されていないA(1),A(3)解放 print("E")
(edited)A B deinit: A(4) C D deinit: A(1) deinit: A(3) Estderr:main.swift:12:7: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x ~~~~^ _A B deinit: A(4) C D deinit: A(1) deinit: A(3) Estderr:main.swift:12:9: warning: initialization of immutable value 'y' was never used; consider replacing with assignment to '_' or removing it let y = x // yは[A(1), A(2), A(3)]を保持x[2] = A(5) でコピーが発生してなかったんですねDictionary にある Key のエントリーが含まれるかどうかを調べる一番いい方法って dictionary.keys.contains(key) でいいんでしたっけ? O(1) になることのドキュメントが見つけられず・・・。dictionary[key] != nil は不細工なので。index(forKey:) が目当てのメソッドじゃないしょうか?Keys 実装目的の最初に挙げられているくらいなので、O(1) だと思うんですが、実装追えてないです。index(forKey:) についての話です。dictionary[key] != nil は一番わかりやすいと思いますね。Dictionary.Keys.contains(_:) は最終これが呼ばれるので、O(1) ですね。(Nativeの場合) (edited)index(forKey:) と同じってこと?_variantBuffer.index(forKey:) は結局 Dictionary.index(forKey:) と同じですね。keys は struct 1つ作るだけだからそんなにオーバーヘッドなさそう https://github.com/apple/swift/blob/82226642c2459c0f5d2054fe1f6545b57efafffb/stdlib/public/core/Dictionary.swift#L1169-L1173index(for:) にしても nil と比較することになると思うんで、ダイレクトにキーが存在するかを調べているということをコード上で表し、かつオーバーヘッドの少ない(最低でも O(1) の)方法はなんだろうということでした。dictionary.keys.contains(key) 使うことにします。_customContainsEquatableElement が mayBeGet(key) != nil を直接使わず index(forKey: key) != nil なのは何故なんだろう。 (edited)NSDictionary をラップしてたら O(1) じゃなくなってる?? (edited)NSDictionary をラップしてないときは、 _variantBuffer が NativeDictionaryBuffer で、それだと、 index(forKey:) も maybeGet も _find を呼ぶことになるので同じですよね。(値を取得しない分、 index(forKey:) の方が得)NSDictionary をラップしているときは、 _CocoaDictionaryBuffer なので、 index(forKey:) が中で mayBeGet を呼ぶようになっているので、結局同じっぽいような。index(forKey:) のほうが特なのですね。納得。 Cocoa のほうだと、maybeGet で見つかったときに、比較的重い処理 https://github.com/apple/swift/blob/82226642c2459c0f5d2054fe1f6545b57efafffb/stdlib/public/core/Dictionary.swift#L3026-L3036 が入るので、何故なんだろうと思ったのです。index(for:) を呼ばずに https://github.com/apple/swift/blob/82226642c2459c0f5d2054fe1f6545b57efafffb/stdlib/public/core/Dictionary.swift#L3397-L3420 みたいな分岐をして .cocoa のときに maybeGet なり何なりを使って検査するようにすれば NSDictionary をラップしてても dictionary.keys.contains(key) を O(1) にできるような気がするんですが、そんなことはないですか?_VariantDictionaryBuffer にそれ用のメソッドを作って、_customContainsEquatableElement から呼ぶべきだと思いますが、はいキーの有無だけであれば O(1) にできると思います。 (edited)_VariantDictionaryBuffer 側に containsKey みたいなメソッドを作ってそれを呼ぶのがいいんでしょうか。 (edited)NSDictionary をラップしてても O(1) になってる? @swiftbot
import Foundation let native1M = [String: String](uniqueKeysWithValues: (1...1_000_000).map { ("\($0)", "\($0)") }) let cocoa1M = (native1M as NSDictionary) as! [String: String] let native2M = [String: String](uniqueKeysWithValues: (1...2_000_000).map { ("\($0)", "\($0)") }) let cocoa2M = (native2M as NSDictionary) as! [String: String] let n = 10 func measure(_ operation: () -> ()) { var sum: TimeInterval = 0 for i in 1...n { let start = Date.timeIntervalSinceReferenceDate operation() let end = Date.timeIntervalSinceReferenceDate let interval = end - start print("\(i): \(interval)") sum += interval } print("avg: \(sum / TimeInterval(n))") } let dictionaries: [(String, [String: String])] = [ ("Native 1M", native1M), ("Cocoa 1M", cocoa1M), ("Native 2M", native2M), ("Cocoa 2M", cocoa2M), ] for (name, dictionary) in dictionaries { print(name) measure { for i in 1...100 { _ = dictionary.keys.contains("\(i)") } } }Swift version 4.1 (swift-4.1-RELEASE)/usercode/main.swift:4:16: error: cannot convert value of type '[String : String]' to type 'NSDictionary' in coercion let cocoa1M = (native1M as NSDictionary) as! [String: String] ^~~~~~~~ /usercode/main.swift:6:16: error: cannot convert value of type '[String : String]' to type 'NSDictionary' in coercion let cocoa2M = (native2M as NSDictionary) as! [String: String] ^~~~~~~~as! [String: String] の時点でどうもNativeに変換されているっぽいですね。import Foundation let native1M = [NSString: NSString](uniqueKeysWithValues: (1...1_000_000).map { ("\($0)" as NSString, "\($0)" as NSString) }) let cocoa1M = NSDictionary(dictionary: native1M) as! [NSString: NSString] let native2M = [NSString: NSString](uniqueKeysWithValues: (1...2_000_000).map { ("\($0)" as NSString, "\($0)" as NSString) }) let cocoa2M = NSDictionary(dictionary: native2M) as! [NSString: NSString] let n = 10 func measure(_ operation: () -> ()) { var sum: TimeInterval = 0 for i in 1...n { let start = Date.timeIntervalSinceReferenceDate operation() let end = Date.timeIntervalSinceReferenceDate let interval = end - start print("\(i): \(interval)") sum += interval } print("avg: \(sum / TimeInterval(n))") } let dictionaries: [(String, [NSString: NSString])] = [ ("Native 1M", native1M), ("Cocoa 1M", cocoa1M), ("Native 2M", native2M), ("Cocoa 2M", cocoa2M), ] for (name, dictionary) in dictionaries { print(name) measure { for i in 1...100 { _ = dictionary.keys.contains("\(i)" as NSString) } } } これで確認できました。 (edited)Native 1M 1: 0.00014901161193847656 2: 9.000301361083984e-05 3: 8.797645568847656e-05 4: 8.702278137207031e-05 5: 8.809566497802734e-05 6: 8.702278137207031e-05 7: 8.797645568847656e-05 8: 8.702278137207031e-05 9: 8.702278137207031e-05 10: 8.809566497802734e-05 avg: 9.392499923706054e-05 Cocoa 1M 1: 1.5260519981384277 2: 1.4988809823989868 3: 1.4609719514846802 4: 1.4877859354019165 5: 1.4853370189666748 6: 1.4784049987792969 7: 1.4788520336151123 8: 1.4836270809173584 9: 1.5162140130996704 10: 1.471932053565979 avg: 1.4888058066368104 Native 2M 1: 9.799003601074219e-05 2: 7.700920104980469e-05 3: 7.605552673339844e-05 4: 7.605552673339844e-05 5: 7.700920104980469e-05 6: 7.700920104980469e-05 7: 7.593631744384766e-05 8: 7.700920104980469e-05 9: 7.700920104980469e-05 10: 9.09566879272461e-05 avg: 8.020401000976562e-05 Cocoa 2M 1: 2.558851957321167 2: 2.5838489532470703 3: 2.5781790018081665 4: 2.586503028869629 5: 2.5640339851379395 6: 2.611467957496643 7: 2.587472081184387 8: 2.5809329748153687 9: 2.5621449947357178 10: 2.583261013031006 avg: 2.5796695947647095Native 1M 1: 0.0001779794692993164 2: 9.298324584960938e-05 3: 9.000301361083984e-05 4: 8.90493392944336e-05 5: 8.893013000488281e-05 6: 8.90493392944336e-05 7: 0.00011992454528808594 8: 0.0001380443572998047 9: 9.799003601074219e-05 10: 9.59634780883789e-05 avg: 0.00010799169540405273 Cocoa 1M 1: 0.00013005733489990234 2: 9.799003601074219e-05 3: 9.799003601074219e-05 4: 9.799003601074219e-05 5: 9.799003601074219e-05 6: 9.799003601074219e-05 7: 9.799003601074219e-05 8: 9.906291961669922e-05 9: 9.799003601074219e-05 10: 9.799003601074219e-05 avg: 0.0001013040542602539 Native 2M 1: 0.00010597705841064453 2: 9.000301361083984e-05 3: 9.000301361083984e-05 4: 8.893013000488281e-05 5: 8.90493392944336e-05 6: 8.90493392944336e-05 7: 8.893013000488281e-05 8: 9.000301361083984e-05 9: 9.000301361083984e-05 10: 8.90493392944336e-05 avg: 9.109973907470704e-05 Cocoa 2M 1: 0.00010001659393310547 2: 9.799003601074219e-05 3: 9.799003601074219e-05 4: 9.906291961669922e-05 5: 9.799003601074219e-05 6: 9.799003601074219e-05 7: 9.799003601074219e-05 8: 9.799003601074219e-05 9: 9.894371032714844e-05 10: 9.799003601074219e-05 avg: 9.839534759521484e-05dictionary.keys.contains(key) ですが、↑の PR がマージされたので NSDictionary をラップしてる場合でも O(1) になりましたNSDictionary をラップしているときに)それを使わないようにして O(n) を回避しているので、 index(for:) はそのままのはずですね。 (edited)index(for:) からそれが呼ばれてるのだと思ってました。_variantBuffer の index(for:) を呼ぶ代わりに↓を呼ぶようになっています。 internal func containsKey(_ key: Key) -> Bool { if _fastPath(guaranteedNative) { return asNative.index(forKey: key) != nil } switch self { case .native: return asNative.index(forKey: key) != nil #if _runtime(_ObjC) case .cocoa(let cocoaBuffer): return SelfType.maybeGetFromCocoaBuffer(cocoaBuffer, forKey: key) != nil #endif } }index(for:) が呼ばれますが、 NSDictionary をラップしているときは maybeGetFromCocoaBuffer になります。 (edited)@compilerEvaluable いいね。今はllvmが特定のc関数についてやってるだけなのかな? https://forums.swift.org/t/pitch-compile-time-constant-expressions-for-swift/12879Authors: Marc Rasi, Chris Lattner から。Types This model supports: builtin integer type tuples of compiler-representable types user-defined structs whose fields are compiler-representable types, which are defined in the current module or which otherwise have a known/fixed layout. metatypes addresses of stack objectsRandom が struct っぽくない挙動をする・・・。 var a = Random.default var b = a print(a.next()) print(b.next())
2190522142258413190 6214093798825593358seed を持つようにしただけだと、 Random.default.next() と呼び出さない限り毎回初期化されちゃうから、Random.default の度に色々やんないといけなくなっちゃう。struct なのがミスリーディングな気も。 class の方がよい?Generators as reference vs value types I think we should require Generators to be classes. It simplifies calling the functions and I don’t see any clear advantage of using struct Generators.func foo<S : Sequence>(_ sequence: S) { ... } let a: Any = ... // a が Sequence か調べて Sequence のときは foo に渡すにはどうすればいい?Any に限らず Foo の中から BarProtocol を満たすものだけを渡したいってのができない。as して渡すだけだから、型として表に見えなくていい。if let seq = a as Sequence { // でもこれができちゃうよ print(type(of: seq)) foo(seq) } (edited)<S : Sequence> に渡せるようにするだけの限定的なやつAny<Sequence where .Iterator.Element == String> この表記、 型パラメータを一つ取るジェネリック型Anyがあるように見えてしまう気がしてきたAny<FooProtocol> にしようという話があって、その時代に書かれたものだから。protocol<A, B> を Any<A, B> にしようってだけだったか。 https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#renaming-protocol-to-any-Any<A, B> が A & B になった以上、 generilized existential の構文も変更しないといけない気がする。それ以前に、あまり導入する気もなさそうな・・・。let a: Sequence where Element == Int & Foo みたいになりそうだ。struct Foo { // method func a() -> Int { return 2 } // computed property var b: Int { return 3 } // stored property let c: Int = 5 } let foo = Foo() print(Foo.a(foo)()) // OK print(Foo.b(foo)) // NG print(Foo.c(foo)) // NGprint(foo[keyPath: \Foo.b])func get<T, U>(_ keyPath: KeyPath<T, U>) -> (T) -> (U) { return { value in return value[keyPath: keyPath] } } struct A { let a: Int } let x = get(\A.a) print(x(A(a: 3))) (edited)KeyPath 、ちょっと合ってるか自信ないですが、↓になってほしくないですか? class Animal { var a: Int = 42 } class Cat : Animal {} let k1: WritableKeyPath<Animal, Int> = \.a let k2: KeyPath<Animal, Int> = k1 // OK let k3: PartialKeyPath<Animal> = k2 // OK let k4: AnyKeyPath = k3 // OK let k1b: WritableKeyPath<Animal, Any> = k1 // NG: これはできなくてよい let k2b: KeyPath<Animal, Any> = k2 // NG: これはできてほしい let k1c: WritableKeyPath<Cat, Int> = k1 // NG: これはできてほしい let k2c: KeyPath<Cat, Int> = k2 // NG: これはできてほしい let k3c: PartialKeyPath<Cat> = k3 // NG: これはできてほしいclass WritableKeyPath<in Root, Value> : ... { ... } class KeyPath<in Root, out Value> : ... { ... } class PartialKeyPath<in Root> : ... { ... } class AnyKeyPath { ... } (edited)KeyPath に WritableKeyPath を結合しても KeyPath であってほしい気が。 https://developer.apple.com/documentation/swift/keypath/2945102-appendingstruct Foo { var a: Int } struct Bar { var foo: Foo } let k1: KeyPath<Bar, Foo> = \.foo let k2: WritableKeyPath<Foo, Int> = \.a let k: WritableKeyPath<Bar, Int> = k1.appending(path: k2)struct Foo { var a: Int } struct Bar { var foo: Foo } let k1: KeyPath<Bar, Foo> = \.foo let k2: WritableKeyPath<Foo, Int> = \.a let k: WritableKeyPath<Bar, Int> = k1.appending(path: k2)swift-4.1.1-RELEASE/usercode/main.swift:11:39: error: cannot convert value of type 'KeyPath<Bar, Int>' to specified type 'WritableKeyPath<Bar, Int>' let k: WritableKeyPath<Bar, Int> = k1.appending(path: k2) ~~~^~~~~~~~~~~~~~~~~~~ as! WritableKeyPath<Bar, Int>struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2)struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2)swift-4.1.1-RELEASE/usercode/main.swift:11:49: error: cannot convert value of type 'PartialKeyPath<Foo>' to expected argument type 'ReferenceWritableKeyPath<_, _>' let k: PartialKeyPath<Bar> = k1.appending(path: k2) ^~struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a // let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() // let a: Int = bar[keyPath: k1][keyPath: k2] let a: Any = bar[keyPath: k1][keyPath: k2] print(a) // OK: 42struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } let k1: KeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a // let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() // let a: Int = bar[keyPath: k1][keyPath: k2] let a: Any = bar[keyPath: k1][keyPath: k2] print(a) // OK: 42swift-4.1.1-RELEASE42KeyPath に WritableKeyPath を append して WritableKeyPath 生成するメソッドが(ドキュメント上?)できてしまってるの↓のせいか。 where 以下がコメントアウト。 extension _AppendKeyPath /* where Self == WritableKeyPath<T,U> */ {
https://github.com/apple/swift/blob/master/stdlib/public/core/KeyPath.swift#L1812(Writable)KeyPath に PartialKeyPath を append して PartialKeyPath を得るの、とりあえず無理やりで extension 書けた。 @swiftbot
struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } extension KeyPath { func appending(path: PartialKeyPath<Value>) -> PartialKeyPath<Root> { return (self as AnyKeyPath).appending(path: path as AnyKeyPath)! as! PartialKeyPath<Root> } } let k1: WritableKeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() let a: Any = bar[keyPath: k] print(a)struct Foo { var a: Int = 42 } struct Bar { var foo: Foo = Foo() } extension KeyPath { func appending(path: PartialKeyPath<Value>) -> PartialKeyPath<Root> { return (self as AnyKeyPath).appending(path: path as AnyKeyPath)! as! PartialKeyPath<Root> } } let k1: WritableKeyPath<Bar, Foo> = \.foo let k2: PartialKeyPath<Foo> = \.a let k: PartialKeyPath<Bar> = k1.appending(path: k2) let bar = Bar() let a: Any = bar[keyPath: k] print(a)swift-4.1.1-RELEASE42didSet の中で didSet は呼ばれないけど・・・ struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 foo.count += 100 } } } var bar = Bar() bar.foo.a = -1 (edited)struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 foo.count += 100 } } } var bar = Bar() bar.foo.a = -1swift-4.1.1-RELEASEFoo.a didSet: 0 Bar.foo didSet: 0, 1struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 // foo.count += 100 self[keyPath: \Bar.foo].count += 100 } } } var bar = Bar() bar.foo.a = -1 (edited)struct Foo { var count: Int = 0 var a: Int = 42 { didSet { print("Foo.a didSet: \(count)") count += 1 } } } struct Bar { var count: Int = 0 var foo: Foo = Foo() { didSet { print("Bar.foo didSet: \(count), \(foo.count)") count += 1 // foo.count += 100 self[keyPath: \Bar.foo].count += 100 } } } var bar = Bar() bar.foo.a = -1swift-4.1.1-RELEASEFoo.a didSet: 0 Bar.foo didSet: 0, 1 Bar.foo didSet: 1, 101 Bar.foo didSet: 2, 201 Bar.foo didSet: 3, 301 Bar.foo didSet: 4, 401 Bar.foo didSet: 5, 501 Bar.foo didSet: 6, 601 Bar.foo didSet: 7, 701 Bar.foo didSet: 8, 801 Bar.foo didSet: 9, 901 Bar.foo didSet: 10, 1001 Bar.foo didSet: 11, 1101 Bar.foo didSet: 12, 1201 Bar.foo didSet: 13, 1301 Bar.foo didSet: 14, 1401 Bar.foo didSet: 15, 1501 Bar.foo didSet: 16, 1601 Bar.foo didSet: 17, 1701 Bar.foo didSet: 18, 1801 Bar.foo didSet: 19, 1901 Bar.foo didSet: 20, 2001 Bar.foo didSet: 21, 2101 Bar.foo didSet: 22, 2201 Bar.foo didSet: 23, 2301 Bar.foo didSet: 24, 2401 Bar.foo didSet: 25, 2501 Bar.foo didSet: 26, 2601 Bar.foo didSet: 27, 2701 Bar.foo didSet: 28, 2801 Bar.foo didSet: 29, 2901 Bar.foo didSet: 30, 3001 Bar.foo didSet: 31, 3101 Bar.foo didSet: 32, 3201 Bar.foo didSet: 33, 3301 Bar.foo didSet: 34, 3401 Bar.foo didSet: 35, 3501 Bar.foo didSet: 36, 3601 Bar.foo didSet: 37, 3701 Bar.foo didSet: 38, 3801 Bar.foo didSet: 39, 3901 .../usr/bin/swift[0x3f24d64] /usr/bin/swift[0x3f250a6] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7fb6ec227390] /usr/lib/swift/linux/libswiftCore.so(swift_conformsToProtocol+0x3e)[0x7fb6e840373e] /usr/lib/swift/linux/libswiftCore.so(+0x39c05f)[0x7fb6e83f505f] /usr/lib/swift/linux/libswiftCore.so(+0x39b82d)[0x7fb6e83f482d] /usr/lib/swift/linux/libswiftCore.so(+0x2e518a)[0x7fb6e833e18a] Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret /usercode/main.swift -disable-objc-interop -I /Libraries/All/.build/release -module-name main -lAll Segmentation fault (core dumped)struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") update() print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 mutating func update() { name = "jiro" } } var b = Stone() b.name = "taro"struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") update() print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 mutating func update() { name = "jiro" } } var b = Stone() b.name = "taro"swift-4.1.1-RELEASEname.didSet begin: -> taro name.didSet begin: taro -> jiro name.didSet begin: jiro -> jiro name.didSet begin: jiro -> jiro name.didSet begin: jiro -> jiro name.didSet end: jiro -> jiro name.didSet end: jiro -> jiro name.didSet end: jiro -> jiro name.didSet end: taro -> jiro name.didSet end: -> taroname の mutating func を Stone の didSet から呼び出した場合の話だった。それだと直接編集するのと同じだから再帰しなくて当然か。struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") name.append("x") print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 } var b = Stone() b.name = "taro"struct Stone { var name: String = "" { didSet { let newValue = name if recCount == 5 { return } recCount += 1 print("name.didSet begin: \(oldValue) -> \(newValue)") name.append("x") print("name.didSet end: \(oldValue) -> \(newValue)") } } var recCount = 0 } var b = Stone() b.name = "taro"swift-4.1.1-RELEASEname.didSet begin: -> taro name.didSet end: -> tarostruct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo?.a] = -1 // ⛔ print(bar.foo!.a)struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo?.a] = -1 // ⛔ print(bar.foo!.a)swift-4.1.1-RELEASE/usercode/main.swift:10:27: error: cannot assign to immutable expression of type 'Int?' bar[keyPath: \Bar.foo?.a] = -1 // ⛔struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar.foo?.a = -1 // ✅ print(bar.foo!.a)struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar.foo?.a = -1 // ✅ print(bar.foo!.a)swift-4.1.1-RELEASE-1Joe Groff added a comment - 7 Sep 2017 2:49 AM This is by design. You can't write back through optional chains, since there's nowhere to write to if the element is nil.extension Optional { // Produce the `default` value if `self` is nil. subscript(default value: Wrapped) -> Wrapped { get { return self ?? value } set { self = newValue } } // Act like optional chaining on read, while allowing "writes" that drop // the value on the floor if `self` is nil. subscript<T>(droppingWritesOnNil path: WritableKeyPath<Wrapped, T>) -> T? { get { return self?[keyPath: path] } set { if let newValue = newValue { self?[keyPath: path] = newValue } } } }
KeyPath で記述できました。 @swiftbot
extension Optional { // Produce the `default` value if `self` is nil. subscript(default value: Wrapped) -> Wrapped { get { return self ?? value } set { self = newValue } } // Act like optional chaining on read, while allowing "writes" that drop // the value on the floor if `self` is nil. subscript<T>(droppingWritesOnNil path: WritableKeyPath<Wrapped, T>) -> T? { get { return self?[keyPath: path] } set { if let newValue = newValue { self?[keyPath: path] = newValue } } } } struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = -1 print(bar.foo!.a) bar.foo = nil bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = 999 print(String(describing: bar.foo)) (edited)extension Optional { // Produce the `default` value if `self` is nil. subscript(default value: Wrapped) -> Wrapped { get { return self ?? value } set { self = newValue } } // Act like optional chaining on read, while allowing "writes" that drop // the value on the floor if `self` is nil. subscript<T>(droppingWritesOnNil path: WritableKeyPath<Wrapped, T>) -> T? { get { return self?[keyPath: path] } set { if let newValue = newValue { self?[keyPath: path] = newValue } } } } struct Foo { var a: Int = 42 } struct Bar { var foo: Foo? = Foo() } var bar = Bar() bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = -1 print(bar.foo!.a) bar.foo = nil bar[keyPath: \Bar.foo[droppingWritesOnNil: \.a]] = 999 print(String(describing: bar.foo))swift-4.1.1-RELEASE-1 nilweak でイミュータビリティが壊れた。 @swiftbot
struct Weak<Value : AnyObject> { weak var value: Value? { didSet { print("Weak.value didSet") } } init(_ value: Value) { self.value = value } } extension Weak : Equatable where Value : Equatable { static func ==(lhs: Weak<Value>, rhs: Weak<Value>) -> Bool { return lhs.value == rhs.value } } extension Weak : Hashable where Value : Hashable { var hashValue: Int { return value?.hashValue ?? 0 } } class Foo : Hashable { static func ==(lhs: Foo, rhs: Foo) -> Bool { return lhs === rhs } var hashValue: Int { return ObjectIdentifier(self).hashValue } } var dict: [Weak<Foo>: Int] = [:] var weak1: Weak<Foo>! var weak2: Weak<Foo>! do { let foo1 = Foo() weak1 = Weak(foo1) dict[weak1] = 111 print("weak1: \(weak1)") let foo2 = Foo() weak2 = Weak(foo2) dict[weak2] = 222 print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))") } print("---") print("weak1: \(weak1)") print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))") (edited)struct Weak<Value : AnyObject> { weak var value: Value? { didSet { print("Weak.value didSet") } } init(_ value: Value) { self.value = value } } extension Weak : Equatable where Value : Equatable { static func ==(lhs: Weak<Value>, rhs: Weak<Value>) -> Bool { return lhs.value == rhs.value } } extension Weak : Hashable where Value : Hashable { var hashValue: Int { return value?.hashValue ?? 0 } } class Foo : Hashable { static func ==(lhs: Foo, rhs: Foo) -> Bool { return lhs === rhs } var hashValue: Int { return ObjectIdentifier(self).hashValue } } var dict: [Weak<Foo>: Int] = [:] var weak1: Weak<Foo>! var weak2: Weak<Foo>! do { let foo1 = Foo() weak1 = Weak(foo1) dict[weak1] = 111 print("weak1: \(weak1)") let foo2 = Foo() weak2 = Weak(foo2) dict[weak2] = 222 print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))") } print("---") print("weak1: \(weak1)") print("weak2: \(weak2)") for (key, value) in dict { print("\(key) -> \(value)") } print("dict[weak1]: \(String(describing: dict[weak1]))") print("dict[weak2]: \(String(describing: dict[weak2]))")swift-4.1.1-RELEASEweak1: Optional(main.Weak<main.Foo>(value: Optional(main.Foo))) weak2: Optional(main.Weak<main.Foo>(value: Optional(main.Foo))) Weak<Foo>(value: Optional(main.Foo)) -> 222 Weak<Foo>(value: Optional(main.Foo)) -> 111 dict[weak1]: Optional(111) dict[weak2]: Optional(222) --- weak1: Optional(main.Weak<main.Foo>(value: nil)) weak2: Optional(main.Weak<main.Foo>(value: nil)) Weak<Foo>(value: nil) -> 222 Weak<Foo>(value: nil) -> 111 dict[weak1]: nil dict[weak2]: nilstruct は weak プロパティを持てないのがいいんじゃないかと思います。weak は GC であっても必要になるんじゃないでしょうか?KeyPath のパターンマッチしたくないですか?今だと KeyPath から構成要素を取り出す方法がないので。 struct Foo { var values: [Int] = [2, 3, 5] } let foo = Foo() let path = \Foo.values[0] print(foo[keyPath: path]) switch path { case \Foo.values[let i]: print(i) default: print("default") }import Foundation func darwinStyleFoo(_ size: Int = 32) { print("darwin style foo") } func unknownStyleFoo(_ size: Int = 32) { print("unknown style foo") } #if _runtime(_ObjC) let foo = darwinStyleFoo #else let foo = unknownStyleFoo #endif foo(32) //OK // foo() // NG@_silgen(…)みたいな裏技は無いかなあ。import Foundation func darwinStyleFoo(_ size: Int) { print("darwin style foo") } func unknownStyleFoo(_ size: Int) { print("unknown style foo") } #if _runtime(_ObjC) let _foo = darwinStyleFoo #else let _foo = unknownStyleFoo #endif func foo(_ size: Int = 32) { _foo(size) } こういうスタイルはどうですか?darwinStyleFoo と unknownStyleFoo は明示的にどちらも呼べるようにしたいfoo を呼ぶとプラットフォームによって適切な方が呼ばれるようにしたい(その場合でもデフォルト引数を設定したい)foo を呼んだ場合でもスタックトレースは darwinStyleFoo 等と同じにしたいdarwinStyleFoo 等のコードを foo の中にも展開するとかでしょうか・・・。[StackTraceElement] がとれて間の一つを取り除けたとしても、 foo を呼び出しているのにその次が darwinStyleFoo になって整合性が崩れそう。class X { static let defaultArgument = 1 func foo(_ bar: Int = defaultArgument) { ... } } こういうのいけるはずdefault2でGeneric parameter 'T' could not be inferredとなる理由が分からない。 @swift-4.2.4
import Foundation infix operator ∘ : CompositionPrecedence precedencegroup CompositionPrecedence { associativity: left higherThan: TernaryPrecedence } public struct Converter<T, U> { public typealias Handler = (T) -> U let handler: Handler public init(_ handler: @escaping Handler) { self.handler = handler } public func compose<A>(_ other: Converter<A, T>) -> Converter<A, U> { return .init { self.handler(other.handler($0)) } } public static func ∘ <A, B, C>(_ lhs: Converter<B, C>, _ rhs: Converter<A, B>) -> Converter<A, C> { return .init { lhs.handler(rhs.handler($0)) } } } public typealias Symbol = (String, String) public typealias Demangler = Converter<Symbol, Symbol> public typealias SymbolFormatter = Converter<Symbol, String> // Demangler extension Converter where T == Symbol, U == Symbol { #if os(macOS) public static let `default` = Converter { ($0.0.uppercased(), $0.1) } #elseif os(Linux) public static let `default` = Converter { ($0.0.lowercased(), $0.1) } #endif } // SymbolFormatter extension Converter where T == Symbol, U == String { #if os(macOS) public static let defaultStyle = Converter { "macOS: \($0)" } #elseif os(Linux) public static let defaultStyle = Converter { "linux: \($0)" } #endif public static let default1 = SymbolFormatter.defaultStyle.compose(Demangler.default) public static let default2 = SymbolFormatter.defaultStyle ∘ Demangler.default } (edited)<stdin>:49:63: error: generic parameter 'T' could not be inferred public static let default2 = SymbolFormatter.defaultStyle ∘ Demangler.default <stdin>:22:24: note: in call to operator '∘' public static func ∘ <A, B, C>(_ lhs: Converter<B, C>, _ rhs: Converter<A, B>) -> Converter<A, C> { (edited) public static func ∘ <A>(_ lhs: Converter<T, U>, _ rhs: Converter<A, T>) -> Converter<A, U> { return .init { lhs.handler(rhs.handler($0)) } }let a: Range<Int> = 1..<10 print(a.map { $0 * 2 })swift-4.1.1-RELEASE/usercode/main.swift:2:7: error: value of type 'Range<Int>' has no member 'map' print(a.map { $0 * 2 }) ^ ~~~Sequence や Collection を conform してる。Range が Sequence じゃないのか。let a: CountableRange<Int> = 1..<10 print(a.map { $0 * 2 })let a: CountableRange<Int> = 1..<10 print(a.map { $0 * 2 })swift-4.1.1-RELEASE[2, 4, 6, 8, 10, 12, 14, 16, 18]2018-05-29-a 2018-05-08-a 2018-05-02-a 2018-04-25-a 2018-04-23-a 4.1.1 4.1 4.0.3 4.0.2 4.0 3.1.1 3.1 3.0.2 3.0.1let a: Range<Int> = 1..<10 print(a.map { $0 * 2 })let a: Range<Int> = 1..<10 print(a.map { $0 * 2 })swift-4.2-DEVELOPMENT-SNAPSHOT-2018-05-29-a[2, 4, 6, 8, 10, 12, 14, 16, 18]extension Never: Equatable { public static func == (lhs: Never, rhs: Never) -> Bool { switch (lhs, rhs) { } } } let a: [Never] = [] print(a == a) // OK
https://github.com/apple/swift-evolution/blob/master/proposals/0215-conform-never-to-hashable-and-equatable.md (edited)extension Never: Equatable { public static func == (lhs: Never, rhs: Never) -> Bool { switch (lhs, rhs) { } } } let a: [Never] = [] print(a == a) // OKswift-4.1.1-RELEASEtruefunc a(a: Int, b: String, c: Never) -> Float { }swift-4.1.1-RELEASE/usercode/main.swift:2:1: error: missing return in a function expected to return 'Float' } ^Error も付けてほしいですよね。 Equatable と Hashable だけでなくて。extension Never : Error {} enum Result<T, E: Error> { case success(T) case failue(E) } let a: Result<Int, Never> = .success(42) print(a)extension Never : Error {} enum Result<T, E: Error> { case success(T) case failue(E) } let a: Result<Int, Never> = .success(42) print(a)swift-4.1.1-RELEASEsuccess(42)NoError じゃなくても Never : Error 自分で後付でもいいのか。struct Cat { var name: String? func nya() { if let name = name { print(name) } } }struct Cat { var name: String? func nya() { if let name = name { print(name) } } }swift-4.1.1-RELEASE2018-05-29-a 2018-05-08-a 2018-05-02-a 2018-04-25-a 2018-04-23-a 4.1.1 4.1 4.0.3 4.0.2 4.0 3.1.1 3.1 3.0.2 3.0.1struct Cat { var name: String? func nya() { if let name = name { print(name) } } }struct Cat { var name: String? func nya() { if let name = name { print(name) } } }swift-3.0.2-RELEASEswift-3.1.1-RELEASEswift-4.0.3-RELEASEif let name = self.name { } じゃないといけなかった気がするんだけど勘違い・・・?selfは不要だったと思う。struct Cat { func name() -> String { return "tama" } func nya() { let name = self.name() } }struct Cat { func name() -> String? { return "tama" } func nya() { if let name = name() {} } }let var = exp と if let var = exp では 右辺と左辺の名前衝突ルールが違うんですね。var image: Image<RGBA<UInt8>> = Image(nsImage: imageView.image!) image = Image(image[1000..<1640, 1000..<1640]) imageView.image! = image.nsImageimport Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in let socket = self!.socket socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1) (edited)import Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in let socket = self!.socket socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1)swift-4.1.1-RELEASEUDPSocket deinit. Socket deinit.import Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in // let socket = self!.socket self?.socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1)import Foundation class UDPSocket { let queue = DispatchQueue(label: "socket") let socket = Socket() func noreturn() { queue.async { [weak self] in // let socket = self!.socket self?.socket.noreturn() } } deinit { socket.canceled = true print("UDPSocket deinit.") } } class Socket { var canceled = false func noreturn() { while !canceled { } } deinit { print("Socket deinit.") } } var udp: UDPSocket? = UDPSocket() udp!.noreturn() sleep(1) udp = nil sleep(1)swift-4.1.1-RELEASEself?.hoge.fuga() この式はif let sself = self { sself.hoge.fuga() } こう展開されてる。if let sself = self { let hoge = sself.hoge hoge.fuga() }self?. を使うとif let sself = self { sself.hoge.fuga() } という展開だとしたら、 sself が強参照もってしまうので、だめじゃないですか? queue.async { [weak socket] in socket?.noreturn() } queue.async { [weak self] in let socket = self?.socket socket.noreturn() }strong_retainとstrong_releaseはペアになってないときもあるんですか?strong_retainは1回しか呼ばれてないのにstrong_releaseは2回呼ばれている func foo() -> Int { var i = 1 let closure: ((Int) -> Void) = { _ in } closure(10) return i }
// foo() sil hidden @$S7sample23fooSiyF : $@convention(thin) () -> Int { bb0: %0 = alloc_stack $Int, var, name "i" // users: %3, %13 %1 = integer_literal $Builtin.Int64, 1 // user: %2 %2 = struct $Int (%1 : $Builtin.Int64) // users: %14, %3 store %2 to %0 : $*Int // id: %3 // function_ref closure #1 in foo() %4 = function_ref @$S7sample23fooSiyFySicfU_ : $@convention(thin) (Int) -> () // user: %5 %5 = thin_to_thick_function %4 : $@convention(thin) (Int) -> () to $@callee_guaranteed (Int) -> () // users: %12, %11, %10, %7, %6 debug_value %5 : $@callee_guaranteed (Int) -> (), let, name "closure" // id: %6 strong_retain %5 : $@callee_guaranteed (Int) -> () // id: %7 %8 = integer_literal $Builtin.Int64, 10 // user: %9 %9 = struct $Int (%8 : $Builtin.Int64) // user: %10 %10 = apply %5(%9) : $@callee_guaranteed (Int) -> () strong_release %5 : $@callee_guaranteed (Int) -> () // id: %11 strong_release %5 : $@callee_guaranteed (Int) -> () // id: %12 dealloc_stack %0 : $*Int // id: %13 return %2 : $Int // id: %14 } // end sil function '$S7sample23fooSiyF'thin_to_thick_functionがretainされたモノを返すのでは。_ = { (_: Int) -> Void in }sil_stage canonical import Builtin import Swift import SwiftShims // main sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 { bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): // function_ref closure #1 in %2 = function_ref @$S4mainySicfU_ : $@convention(thin) (Int) -> () // user: %3 %3 = thin_to_thick_function %2 : $@convention(thin) (Int) -> () to $@callee_guaranteed (Int) -> () // user: %4 strong_release %3 : $@callee_guaranteed (Int) -> () // id: %4 %5 = integer_literal $Builtin.Int32, 0 // user: %6 %6 = struct $Int32 (%5 : $Builtin.Int32) // user: %7 return %6 : $Int32 // id: %7 } // end sil function 'main' // closure #1 in sil private @$S4mainySicfU_ : $@convention(thin) (Int) -> () { // %0 // user: %1 bb0(%0 : $Int): debug_value %0 : $Int // id: %1 %2 = tuple () // user: %3 return %2 : $() // id: %3 } // end sil function '$S4mainySicfU_'thin_to_thick_functionか。ドキュメントを探すとそれっぽい記述が見つかりました。@ callee_guaranteedが@convention(thick)を示しているの知らなかったので気づきませんでした。ありがとうございます! @convention(thick) indicates a "thick" function reference, which uses the Swift calling convention and carries a reference-counted context object used to represent captures or other state required by the function. This attribute is implied by @callee_owned or @callee_guaranteed. (edited)extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }swift-4.1.1-RELEASE/usercode/main.swift:2:48: warning: redundant same-type constraint 'Wrapped' == 'Int' @_specialize(exported: true, where Wrapped == Int, S == [Int]) ^ /usercode/main.swift:2:58: note: same-type constraint 'S.Element' == 'Int' implied here @_specialize(exported: true, where Wrapped == Int, S == [Int]) ^extension Optional { @_specialize(exported: true, where Wrapped == Int) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }extension Optional { @_specialize(exported: true, where Wrapped == Int) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }swift-4.1.1-RELEASE/usercode/main.swift:2:6: error: too few type parameters are specified in '_specialize' attribute (got 1, but expected 2) @_specialize(exported: true, where Wrapped == Int) ^ /usercode/main.swift:2:6: error: Missing constraint for 'S' in '_specialize' attribute @_specialize(exported: true, where Wrapped == Int) ^extension Optional { @_specialize(exported: true, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }extension Optional { @_specialize(exported: true, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }swift-4.1.1-RELEASE/usercode/main.swift:2:6: error: too few type parameters are specified in '_specialize' attribute (got 1, but expected 2) @_specialize(exported: true, where S == [Int]) ^ /usercode/main.swift:2:6: error: Missing constraint for 'Wrapped' in '_specialize' attribute @_specialize(exported: true, where S == [Int]) ^func の方を消すってどういうこと?func の where を消すと↓ Wraped と S.Element が同じ型である保証がなくなって + ができない。 extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] { return Array(sequence) + (self.map { [$0] } ?? []) } }extension Optional { @_specialize(exported: true, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] { return Array(sequence) + (self.map { [$0] } ?? []) } }swift-4.1.1-RELEASE/usercode/main.swift:4:16: error: generic parameter 'S' could not be inferred return Array(sequence) + (self.map { [$0] } ?? []) ^@_specialize(exported: true, where Wrapped == Int, S == [_]) みたいな記述を認めてほしいね。_ が他の構文の where でも書けるなら Parameterized Extension がなくても↓みたいなのもできるし。 extension Optional where Wrapped == _? { func flatten() -> Wrapped.Wrapped? { return flatMap { $0 } } }@_specialize(exported: true, where Wrapped == Int, S == [Int]) で警告が出なくなるというのもいいけど、記述が冗長であることは否めない。 Int じゃなくて Foo<Bar<Baz>> とかだと 2 回書くのは大変。_ が書けると Higher Kind Type が書けるようになる気がするprotocol がジェネリックじゃないからギリギリ書けない?↓みたいなことが書きたいけど。 protocol Functor { associatedtype Value func map<F>(_ f: (Value) -> F.Value) -> F where F<_> == Self<_> }var foo: Foo? というプロパティを持つ型があって、そのメソッドで↓のようなことをやりたいときにうまい書き方ってありますか? if self.foo == nil { // self.foo の初期化 self.foo = ... } let foo: Foo = self.foo! // 以下、foo を使うコードlazy var foo: Foo = ... にしない理由は何でしょう?class ViewController { var foo: Foo? = nil func bar(_ x: Int) { if self.foo == nil { // self.foo の初期化 self.foo = Foo(x) } let foo: Foo = self.foo! // 以下、foo を使うコード ... } } func bar(_ x: Int) { let foo = self.foo ?? (self.foo = Foo(x), self.foo!).1 (edited)! が残ってるlet foo: Foo = { guard let foo = self.foo else { let foo = ... self.foo = foo return foo } return foo }() よくこれ書いてます!はないけど self.foo が 2回出てくるので それが嫌だったら高階関数用意したほうが良いと思いますextension Optional { mutating func ensure(default val: @autoclosure () -> Wrapped) -> Wrapped { if let v = self { return v } let v = val() self = v return v } } class C { var foo: Foo? func test(x: Int) { let foo = self.foo.ensure(default: Foo(x)) } (edited)func initUnwrap<T>(_ optional: inout T?, initialize: () -> T) -> T { if let value = optional { return value } let value = initialize() optional = value return value } let foo: Foo = initUnwrap(&self.foo) { Foo(x) }inout より @rintaro さんの mutating func の方がキレイかな。mutating func は self が inout 引数を体現してる。ensure 普通に標準ライブラリにほしい。Bool の toggle が通ったことを考えるとこれもいけるのでは?Dictionary の subscript(_:default:) は同じじゃないですか?Bool.toggle は、メソッド単発の小さな改良のための Proposal の意でした。extension Optional { @_specialize(exported: true, kind: partial, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }
kind: partial だと警告消えますね。出力されるコードが同等なのかは調べてないです。extension Optional { @_specialize(exported: true, kind: partial, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } }swift-4.1.1-RELEASEpartial になっちゃってたら微妙ですね・・・。$cat full.swift extension Optional { @_specialize(exported: true, kind: full, where Wrapped == Int, S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } } let a: Int? = 7 let b: [Int] = [2, 3, 5] print(a.foo(b)) $cat partial.swift extension Optional { @_specialize(exported: true, kind: partial, where S == [Int]) func foo<S : Sequence>(_ sequence: S) -> [Wrapped] where S.Element == Wrapped { return Array(sequence) + (self.map { [$0] } ?? []) } } let a: Int? = 7 let b: [Int] = [2, 3, 5] print(a.foo(b)) $swift -O full.swift full.swift:2:60: warning: redundant same-type constraint 'Wrapped' == 'Int' @_specialize(exported: true, kind: full, where Wrapped == Int, S == [Int]) ^ full.swift:2:70: note: same-type constraint 'S.Element' == 'Int' implied here @_specialize(exported: true, kind: full, where Wrapped == Int, S == [Int]) ^ [2, 3, 5, 7] $swift -O partial.swift [2, 3, 5, 7] $md5 full MD5 (full) = 728efb9ffc6814bb17e0eedb72c5cda2 $md5 partial MD5 (partial) = c07fb4573afb1d8ae2ee53f30f341383 (edited)-module-name 揃えないと同じバイナリにならないので$ swiftc -O partial.swift -module-name Test -o partial $ swiftc -O full.swift -module-name Test -o full で試したら一致しました。full の方で特殊化できてなかったらどの道これ以上やりようがないですし、仕方なさそうですね。
protocol Functor { associatedtype Value associatedtype Transformed<T> func map<T>(_ transform: (Value) -> T) -> Transformed<T> } extension Optional { typealias Value = Wrapped typealias Transformed<T> = Optional<T> func map<T>(_ transform: (Wrapped) -> T) -> Optional<T> { ... } } (edited)Functor の制約としては緩いですよね。Self は associatedtype 含めて同一になっちゃうから強すぎて、Self から associatedtype を剥がしたものを記述できる構文が必要なはず。SelfProtocol みたいなものが必要。 protocol Functor { associatedtype Value func map<R: SelfProtocol>(_ transform: (Value) -> R.Value) -> R }Optional は Protocol じゃないもんな。protocol Functor { associatedtype Value func map<R: MyKind>(_ transform: (Value) -> R.Value) -> R }try! や as! は optional! と同様の問題は抱えてるはずで、 !! じゃ不十分というのはその通りだ。 (edited)try! foo.bar()! !! "失敗した理由"try? foo.writeToFile(path) ?? fatalError("Reason")!! が良いらしい。 Symbology mismatchが強調になってるんだけど、たしかに ?? でデフォルト値を与えると見せかけて fatalError で死ぬ っていうのも他の ! 用法と比べて危険性が見えにくい気もする。// https://github.com/apple/swift/blob/9286b3627d90523752cb1a2ffc02f7500ead0ea8/stdlib/public/core/FloatingPoint.swift.gyb#L2405-L2466 func test<T: BinaryFloatingPoint>(range: Range<T>) { print("\(T.self): \(range)") let delta = range.upperBound - range.lowerBound let maxSignificand = T.RawSignificand(1) << (T.significandBitCount + 1) // rand = generator.next(upperBound: maxSignificand) \in 0...maxSignificand-1 let zero: T.RawSignificand = 0 let maxx = maxSignificand - 1 let minimum = T(zero) * .ulpOfOne / 2 print("minimum == 0: \(minimum == 0)") let lb = delta * minimum + range.lowerBound print("lb == range.lowerBound: \(lb == range.lowerBound)") let maximum = T(maxx) * .ulpOfOne / 2 print("maximum < 1: \(maximum < 1)") let ub = delta * maximum + range.lowerBound print("ub < range.upperBound: \(ub < range.upperBound)") print("") } let floatRanges: [Range<Float>] = [1..<2, 2..<4] for range in floatRanges { test(range: range) } let doubleRanges: [Range<Double>] = [1..<2, 2..<4] for range in doubleRanges { test(range: range) }Float: 1.0..<2.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: false Float: 2.0..<4.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: false Double: 1.0..<2.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: false Double: 2.0..<4.0 minimum == 0: true lb == range.lowerBound: true maximum < 1: true ub < range.upperBound: falseFloat.random(1..<2)で2が生成されうることの確認なんですがbugs.swift.orgに投げればいいんですかねFloat.random(in: Float(2.0) - .ulpOfOne ..< Float(2.0)) == Float(2.0) で true 出てしまう。let range = 0..<0 print(Int.random(in: range))Fatal error: Can't get random value with an empty range Current stack trace: 0 libswiftCore.so 0x00007f60e2191750 _swift_stdlib_reportFatalError + 168 1 libswiftCore.so 0x00007f60e1eec56a <unavailable> + 1488234 2 libswiftCore.so 0x00007f60e212859e <unavailable> + 3831198 3 libswiftCore.so 0x00007f60e1eec56a <unavailable> + 1488234 4 libswiftCore.so 0x00007f60e20838e9 <unavailable> + 3156201 5 libswiftCore.so 0x00007f60e1fb13d0 <unavailable> + 2294736 6 libswiftCore.so 0x00007f60e1fb1690 static FixedWidthInteger<>.random(in:) + 27 8 swift 0x0000000001041ffe <unavailable> + 12853246 9 swift 0x0000000001046122 <unavailable> + 12869922 10 swift 0x00000000004f82e2 <unavailable> + 1016546 11 swift 0x00000000004de86b <unavailable> + 911467 12 swift 0x00000000004d9ba0 <unavailable> + 891808 13 swift 0x000000000048a308 <unavailable> + 566024 14 libc.so.6 0x00007f60e52d3740 __libc_start_main + 240 15 swift 0x0000000000487fc9 <unavailable> + 557001 #0 0x0000000004106e24 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x4106e24) #1 0x0000000004104cb2 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4104cb2) #2 0x0000000004106fd2 SignalHandler(int) (/usr/bin/swift+0x4106fd2) #3 0x00007f60e6ba9390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007f60e20838f1 $Ss18_fatalErrorMessage__4file4line5flagss5NeverOs12StaticStringV_A2HSus6UInt32VtFTf4nnddn_n (/usr/lib/swift/linux/libswiftCore.so+0x3028f1) #5 0x00007f60e1fb13d0 $Ss17FixedWidthIntegerPss08UnsignedC09MagnitudeRpzs06SignedC06StrideRpzrlE6random2in5usingxSnyxG_qd__zts21Random0..<0 自体は OK (edited)ClosedRangeRange の場合は Optional 返すでもいい気が。 (edited)let range1 = 0..<1 print(String(describing: range1.first)) print(String(describing: Int.random(in: range1))) let range2 = 0..<0 print(String(describing: range2.first)) // print(String(describing: Int.random(in: range2))) // nil になってほしいOptional(0) 0 nilrange.random() じゃないのかが書いてあるね。でもいまいち英文の意味がわからない・・・。Also, since we decided pretty early on that we're going to trap if for whatever reason we can't get a random number この trap って何を意味してる? signal を trap する話?それとも単にエラーハンドリング的な意味?Collection.random() が空のときに nil を返すことも決定したので、 range.random() を正規の表現にすると、前述の non-nil のポリシーに例外を持たせる事になってしまう (edited)print(String(describing: "abcdefg".randomElement()))Optional("b")String の Index は Int じゃないので。subscript は O(1) だけどcount から最初にインデックスのインデックスを決めて、.count も O(n) なので、なかなか重い処理だ。String って生成時に count も生成してるんじゃないのか・・・。RandomAccessCollection って subscript によるランダムアクセスが O(1) なんじゃなくて(当然これも満たされるけど)、インデックス間の任意個数分の移動が O(1) ってややこしいな・・・。Collection の subscript が O(1) は保証されてるんだっけ・・・。 String は O(1) として。count の一番の用途じゃないの?
Yeah, that is a concern, though it won't happen with non-pathological RNGs =) Maybe for the test use an RNG that instead generates [.min, .max, basic LCG sequence ...].Never to Equatable and Hashable, has been accepted! Never should also conform to Error and Comparable 2. Never should become a blessed bottom type Exactly the resolution I hope...Base が値型だとオーバーヘッド大きそう。 struct Extension<Base> { let base: Base init (_ base: Base) { self.base = base } } protocol ExtensionCompatible { associatedtype Compatible static var ex: Extension<Compatible>.Type { get } var ex: Extension<Compatible> { get } } extension ExtensionCompatible { static var ex: Extension<Self>.Type { return Extension<Self>.self } var ex: Extension<Self> { return Extension(self) } }
https://qiita.com/motokiee/items/e8f07c11b88d692b2cc5UIColor.yamabuki のように書いているExtensionを以下のようにオシャレに書くことができる、と...
var ex: Extension<Self> { return Extension(self) }foo.ex.bar ってやるだけで 80 バイトのコピーが走るってことですよね?set しようとしたら大変ですが。BaseClass があるパターンなので、値型に対してもバラバラに記述しないといけないケースをプロトコルでどうにかならないかなと。Foo: ExComaptible するのと、 共通のProtocolを置くのって、どれも同じことじゃないですか?extension Reactive where Base : P0 { ... } extension Reactive where Base : P1 { ... }Image 型とかで、ピクセル単位でループしてたりするとちょっと許容できなさそうですね。 (edited)Example<Base> が class になってますが、これは struct でないとパフォーマンス的によりまずい気がします(ヒープに確保されるので)。 (edited)ex. だとずいぶん遅くなって謎・・・。何か間違ってんのかなぁ。: ExtensionConvertible するだけだと -c release でも ex が特殊化されてないみたいで、 ex_ に比べて数十倍遅くなってしまった・・・。extension に ex の実装を書き下すと(おそらく) Extension が特殊化されて数倍の遅さ程度になった。Foo が struct で Bar が classmeasure してるので swift test -c release で実行。@inlinableとか使うとtestFooDirectとtestFooExampleは同等になった。 public struct Example<Base> { @usableFromInline let base: Base @inlinable public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { @inlinable public var ex: Example<Self> { return Example(self) } }
extension Foo { @inlinable public var ex_ab: Int { return a + b } } extension Example where Base == Foo { @inlinable public var ab: Int { return base.a + base.b } } (edited)testBarExampleは@inlinableを色々つけてもtestBarDirectより10倍くらい遅い。public final class BarにしたらtestBarExampleとtestBarDirectも同等になった。 (edited)@_specializeでspecialize実装を生成しておくよりも@inlinableを活用すべき、という理解で合ってるのかな?@inlinableはかなり有効ですね。final でないクラスで遅くなるのは、 ex の呼び出しと ex の持つ self 経由での呼び出しとで 2 回動的ディスパッチが走るからでしょうか?で、 final にすると両方静的ディスパッチになるので速くなる。 (edited)Bar が final でなかったら、 bar.ex が動的ですよね。ex が self を呼び出すところは特殊化できないなら動的になって、self.ab するところが動的か静的か。 let bar = Bar() var sum = 0 for _ in 0..<N { sum += bar.ex.ab }ab は Extension だからオーバーライドできないことが保証されて静的になるってことですね。final 付けるかどうかの差は、 ex 関係なくて単に bar.ex が動的/静的ディスパッチになっているかの差?bar.ex_ab のときも速くなりそう。 (edited)extension Example where Base == Bar { @inlinable public var ab: Int { return base.a + base.b } } の@inlinable有無で速度が違うし。@inlineable がないとインライン化されないんでしょうか?@inlineable はモジュールをまたいでインライン化可能できるようにする指定子ExPerformanceTestsで使ってるから。Example<T> の self への代入によるコピーコストまでなかったことにはならない気がするんですよねぇ。
extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } }Example<T> が値を保持してるのを参照にしなきゃいけないし。 @nonescaping 的な、即時解放されることが保証されてればコピー省略できるのかなぁ。@inlinableやfinalを活用してのExPerformanceTestsのmeasuredはこんな感じ。 …testBarDirect]' measured [Time, seconds] average: 0.009, … …testBarExample]' measured [Time, seconds] average: 0.010, … …testFooDirect]' measured [Time, seconds] average: 0.004, … …testFooExample]' measured [Time, seconds] average: 0.004, … (edited)…testBarDirect]' measured [Time, seconds] average: 0.018, … …testBarExample]' measured [Time, seconds] average: 0.614, … …testFooDirect]' measured [Time, seconds] average: 0.024, … …testFooExample]' measured [Time, seconds] average: 0.767, …-emit-ir できるんだっけ? (edited)let N = 10_000_000 public struct Example<Base> { let base: Base public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { public var ex: Example<Self> { return Example(self) } } public struct Foo { public var a: Int = 1 public var b: Int = 2 public var c: Int = 3 public var d: Int = 4 public var e: Int = 5 public var f: Int = 6 public var g: Int = 7 public var h: Int = 8 public var i: Int = 9 public var j: Int = 10 public init() {} } extension Foo { public var ex_ab: Int { return a + b } } extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } } let foo = Foo() var sum = 0 for _ in 0..<N { sum += foo.ex.ab } print(foo)swift-4.1.1-RELEASEFoo(a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10)let N = 10_000_000 public struct Example<Base> { let base: Base public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { public var ex: Example<Self> { return Example(self) } } public struct Foo { public var a: Int = 1 public var b: Int = 2 public var c: Int = 3 public var d: Int = 4 public var e: Int = 5 public var f: Int = 6 public var g: Int = 7 public var h: Int = 8 public var i: Int = 9 public var j: Int = 10 public init() {} } extension Foo { public var ex_ab: Int { return a + b } } extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } } let foo = Foo() var sum = 0 for _ in 0..<N { sum += foo.ex.ab } print(foo)<unknown>:0: error: option '-emit-ir' is not supported by 'swift'; did you mean to use 'swiftc'?nal の度にインスタンス作ってるだけでなく、何かを設定するときも大量にインスタンス作ってます、例えばサンプルコード出してみると self.nal.layout(self.viewA) { $0 .setTopCenter(by: { $0.topCenter }) .setWidth(by: { $0.width }) .fitHeight() } 上記画面コンポーネントは viewA だけだけど、作ったインスタンスは nal $0 .setTopCenter $0.topCenter setWidth $0.width .fitHeight の7つあります
.ex形式にしないで 直接extensionを書けば良いと思います。 まあ、今の会話の流れだと、それでもinline化によってコピーが回避されてる説が出てきましたが。let N = 10_000_000 public struct Example<Base> { let base: Base public init(_ base: Base) { self.base = base } } public protocol ExampleCompatible { associatedtype CompatibleType var ex: CompatibleType { get } } extension ExampleCompatible { public var ex: Example<Self> { return Example(self) } } public struct Foo { public var a: Int = 1 public var b: Int = 2 public var c: Int = 3 public var d: Int = 4 public var e: Int = 5 public var f: Int = 6 public var g: Int = 7 public var h: Int = 8 public var i: Int = 9 public var j: Int = 10 public init() {} } extension Foo { public var ex_ab: Int { return a + b } } extension Foo: ExampleCompatible { public var ex: Example<Foo> { return Example(self) } } extension Example where Base == Foo { public var ab: Int { return base.a + base.b } } let foo = Foo() var sum = 0 for _ in 0..<N { sum += foo.ex.ab } print(foo); ModuleID = '-' source_filename = "-" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" %TSi = type <{ i64 }> %T4main3FooV = type <{ %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi, %TSi }> %swift.type = type { i64 } %swift.protocol = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i32, i32, i16, i16, i32 } %swift.refcounted = type { %swift.type*, i64 } %swift.full_boxmetadata = type { void (%swift.refcounted*)*, i8**, %swift.type, i32, i8* } %swift.opaque = type opaque %swift.type_pattern = type opaque %swift.protocol_requirement = type { i32, i32 } %swift.protocol_conformance = type { i32, i32, i32, i32 } %swift.type_metadata_record = type { i32, i32 } %Ts27_ContiguousArrayStorageBaseC = type <{ %swift.refcounted, %Ts10_ArrayBodyV }> %Ts10_ArrayBodyV = type <{ %TSC22_SwiftArrayBodyStorageV }> %TSC22_SwiftArrayBodyStorageV = type <{ %TSi, %TSu }> %TSu = type <{ i64 }> %T4main7ExampleV = type <{}> %T4main7ExampleV.2 = type <{}> %T4main7ExampleVyAA3FooVG = type <{ %T4main3FooV }> @_T04main1NSivp = hidden local_unnamed_addr global %TSi zeroinitializer, align 8 @_T04main3fooAA3FooVvp = hidden local_unnamed_addr global %T4main3FooV zeroinitializer, align 16 @_T04main3sumSivp = hidden local_unnamed_addr global %TSi zeroinitializer, align 8 @_T0s23_ContiguousArrayStorageCyypGML = linkonce_odr hidden local_unnamed_addr global %swift.type* null, align 8 @_T0ypML = linkonce_odr hidden local_unnamed_addr global %swift.type* null, align 8 @_swift_getExistentialTypeMetadata = external local_unnamed_addr global %swift.type* (i1, %swift.type*, i64, %swift.protocol**)* @_swift_allocObject = external local_unnamed_addr global %swift.refcounted* (%swift.type*, i64, i64)* @_T0s27_ContiguousArrayStorageBaseC16countAndCapacitys01_B4BodyVvpWvd = external local_unnamed_addr global i64, align 8 @0 = private constant [11 x i8] c"4main3FooV\00", section "swift3_typeref" @"\01l__swift3_reflection_descriptor" = private constan$ swift demangle S4main7ExampleVA2A3FooVRszlE2abSivg $S4main7ExampleVA2A3FooVRszlE2abSivg ---> (extension in main):main.Example<A where A == main.Foo>.ab.getter : Swift.Intswiftc -O -emit-ir main.swift | grep S4main7ExampleVA2A3FooVRszlE2abSivg するととんでもなく長い行が出てくるdefault じゃなくてきちんと表示されるようになります。 struct StoredDefaultArgument { Expr *DefaultArg = nullptr; Initializer *InitContext = nullptr; StringRef StringRepresentation; };class Example { func test() { let f = { [weak weakSelf = self] in guard let `self` = weakSelf else { return } // Set a breakpoint at here print(self) } f() } } Example().test() 今度は謎の結果に… (lldb) po self error: <EXPR>:3:1: error: use of unresolved identifier 'self' self ^~~~ (lldb) fr var (@lvalue example.Example?) weakSelf = 0x00000001009002b0 (example.Example) self = 0x00000001009002b0 {}self という名前での変数宣言をやめましょうということですね…guard let this = self else { return } は案外賢かったのかもしれない… (edited)zelf 派ですが、あまり zelf の人いないですね・・・。wself と sself と uself (edited)pageControl.rx.controlEvent(.valueChanged) .subscribe(onNext: { [weak self] in guard let currentPage = self?.pageControl.currentPage else { return } guard let contentOffset = self?.scrollView.contentOffset else { return } guard let currentIndex = self?.currentIndex else { return } ...self 派も weakSelf capture 派も救われる…self の再定義をしないのは、デバッグは関係なくて、self に再定義したそのスコープでさらにクロージャーを作るときにself?. 方式だと、 self によらない処理が書かれていると self が解放されていてもそれが走ってしまいませんか?[weak self] を書き忘れた場合などで、 self が強なのか弱なのかゴチャゴチャしてくるからですreturn したいような・・・。{ [weak self] in self?.titleLabel.alpha = 0 self?.titleLabel.transform = CGAffineTransform(translationX: 0, y: labelHeight) } こういう場合はいちいちguard書くよりはself?.の方が私はいいんじゃないかと思います。self?.titleLabel.alpha = 0 のときは生きてるけど self?.titleLabel.transform = ... では死んでることがあり得るかということですよね?guard let していると、途中で解放されたときにそのクロージャからのみキャプチャされている状態になって、それで変なことになることがないかなというのが前から少し気になってました。self に限らず weak キャプチャしたオブジェクトが異なるスレッドで解放される可能性がある場合、リファレンスカウントの減少から deinit の実行までは排他的でもアトミックでもないと思うんですが、そうすると weak キャプチャしてる側で常にぶっ壊れる可能性がないですか?異なるスレッドからの解放はそもそもやっちゃいけない?self?.foo() を呼び出したときに、 self のリファレンスカウントをチェックしたときは 1 だったけど、 foo が呼ばれる前に別スレッドから self が解放されてしまったとか。guard let self = self else { return } してると一見安全そうだけど、この場合でもリファレンスカウントのチェックから代入してリファレンスカウント増やすまでの間に別スレッドから解放されてしまうおそれがありそう。 (edited)self?.foo() は fooを呼び出す前に self を retain するはずですweak の場合のみ?それとも ?. 全般??. は その strong が生きてるので + 1 はいらんですねvar foo: Foo? = Foo() の foo が、 foo?.bar() を呼んでいる間に別スレッドから nil にされてしまう場合とかありそう。?. に限らない? foo.bar() でも foo が書き換えられてインスタンスが解放されることはあり得る?std::atomic ですimport Dispatch class Foo { func bar() {} } var foo: Foo? = Foo() func a() { foo?.bar() foo = nil } func b() { foo?.bar() foo = nil } DispatchQueue.global(qos: .default).async { a() } DispatchQueue.global(qos: .default).async { b() }
let old = refCount() let new do { if (checkZero) { return } new = old + 1 } while ( CAS (old, new) ) ↑シンプルにこういう形ですよ (edited)oldbits は参照なのか。 auto oldbits = refCounts.load(SWIFT_MEMORY_ORDER_CONSUME);oldbits と newbits が値だったら関数ローカルな存在でどことも共有されてないのでそもそも排他する必要がなくないということなんだけど、それ自体は値だけど中にポインタか参照を持ってるのかな?template <typename RefCountBits> class RefCounts { std::atomic<RefCountBits> refCounts; ... } (edited)oldbits と newbits は値型の関数ローカルの変数で、そこに書き戻したのがどうやってオブジェクトが保持している RefCounts に反映されてるの? (edited)while (!refCounts.compare_exchange_weak(oldbits, newbits, std::memory_order_relaxed)); ↑ここのCASで書き戻してるrefCounts と oldBits を比較してるのか。compare_exchange_weak が副作用のあるメソッドだとわかってなかった。pair って入ってると辛そう。let foo = foo(at: path) // 名前が被ってコンパイルエラーself. で済むなら self. を付ける、関数はできるだけ作らない、とかでしょうか?self. をつけて解決してます (edited)func foo(at: Path) を作る。let f = foo(at: path)ですね。 そうじゃなかったらなんとか別の変数名を考える。Decodableのイニシャライザをサブクラスでオーバーライドできる様になってた。 @swift-4.1.3 @swift-4.2.4
import Foundation private class Person: Codable { let name: String init(name: String) { self.name = name } } private class Employee: Person { let id: Int init(name: String, id: Int) { self.id = id super.init(name: name) } enum CodingKeys: String, CodingKey { case id } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(Int.self, forKey: .id) try super.init(from: decoder) } override func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try super.encode(to: encoder) } } (edited)<stdin>:29:19: error: method does not override any method from its superclass override func encode(to encoder: Encoder) throws { ~~~~~~~~ ^ <stdin>:28:1: error: 'required' initializer 'init(from:)' must be provided by subclass of 'Person' ^ <unknown>:0: note: 'required' initializer is declared in superclass here (edited)Encodableのメソッドも同じだった。@dynamicMemberLookup と @dynamicCallable に加えて、 @dynamicConvertible ほしくないですか?今だと PythonObject から Swift に戻すときに↓のようにしないといけなくて面倒。 import Python let np = Python.import("numpy") let a = np.array([[1, 2], [3, 4]]) let b = np.array([[5], [6]]) let c = np.matmul(a, b) let d: Int = Int(c[0, 0])! // ここで明示的変換が必要 print(d)@dynamic 系は安全性を崩して書きやすさをとっているので、 dynamic に限って暗黙の型変換を許すのは良いバランスじゃないかということです。import Python let np = Python.import("numpy") let a = np.array([[1, 2], [3, 4]]) let b = np.array([[5], [6]]) let c = np.matmul(a, b) let d: Int = c[0, 0] // 暗黙の型変換 print(d)Int を右に書くか左に書くかの違いに見えますが、引数に渡すときに意味があります。@dynamicMemberLookup @dynamicCallable @dynamicConvertible struct PythonObject { ... func converted() -> Int { ... } func converted() -> Bool { ... } func converted() -> String { ... } func converted() -> (PythonObject, PythobObject) { ... } ... } (edited)Int や Bool 等については extension の init になってるけど、タプルは extension 作れないので PythonObject に tuple2 とかのメソッドが生えてて統一感もない。converted の例は今適当に書いたから深く考えてなかった。 (edited)@dynamicMemberLookup が付与されているときのみ @dynamicConvertible にできるとか?extension Int { init(_ : py PythonObject) } ↑これの実装+暗黙呼び出しがしたいって感じですかね init側じゃなくてconvert側でもいいけど@dynamicMemberLookup な型を引数にとる init(_:) があれば暗黙型変換とかいう機能でもいいのかもしれないけど (edited)@dynamic 系ってあえてプロトコルじゃなくしてあって@dynamicCallable の subscript の引数って Dictionary じゃなくてもいいし、associatedtype でできるかconverted という名前で解決するパターンだと attribute と親和しそうなんよね。PythonObject は暗黙的に変換されるから
(edited)import Python let np = Python.import("numpy") let elements: [[Int]] = [[1, 2], [3, 4]] let a = np.array(elements) // OK@dynamicCallable の subscript の引数の型が (edited)PythonObject じゃなくて PythonConvertible になってるから。@dynamicCallable はメソッドコールを subscript に変換する。[[Int]] が PythonConvertible として定義されてて、全部それで受け取るのか。 (edited)a = someValue(keyword1: 42, "foo", keyword2: 19) が a = someValue.dynamicallyCall(withKeywordArguments: [ "keyword1": 42, "": "foo", "keyword2": 19 ]) のシュガー。extension Array: PythonConvertible where Element: PythonConvertible { ... } だろうね。
x.toSwiftInt() みたいな事をやったところでnil が返ってくるInt(pythonObject) も変換できなかったら nillet d: Int = c[0, 0] // 暗黙の型変換! や precondition に失敗したときと同じイメージです。std::atomic とか使ってたのでstdlib内を探したら _stdlib_atomicCompareExchangeStrongPtr https://github.com/apple/swift/blob/master/stdlib/public/core/Runtime.swift.gyb#L25 とか見つけたのですが、stdlib内で使ってるところが見当たらず。何か違うメカニズムを使ったほうがよいのかな。LLVMEnablePrettyStackTrace()みたいなのを付け加えようとしてるのです。_stdlib_atomicCompareExchangeStrongPtr使います。func returnStringCollection() -> opaque Collection where _.Element == String みたいなのを出来るようにする提案 (edited)func makeOpaque<T>(_: T.Type) -> opaque Any { /* ... */ } var x = makeOpaque(Int.self) x = makeOpaque(Double.self) // error: "opaque" type from makeOpaque<Double> is distinct from makeOpaque<Int><T> で判断するのかな??makeOpaque の実装知らないと判断できない気がするんですけど、モジュール跨いだときどうするんだろう。(まだ読んでないprotocol P { } extension Int : P { } extension String : P { } func f1() -> opaque P { return "opaque" } func f2() -> opaque P { return 42 } var a = f1() a = f2()opaqueなら実装詳細を隠したままconcrete typeを使ってパフォーマンス出せるよ、って事かな?↓これの最後の行はエラー?? concrete typeに出来なければエラー、みたいな制限がつくのかな? (edited)func f() -> P { if Bool.random() { return 17 } else { return "hello, existential" } } こういうのはできないってことらしいfunc f() -> P { /* ... */ } func g() -> opaque P { /* ... */ } let fArray = [f(), f(), f()] // contains a mix of String and Int at run-time let gArray = [g(), g(), g()] // homogeneous array of g()'s opaque result typelet vf1 = f1() // type of vf1 is the opaque result type of f1()Effect on API resilience Opaque result types are part of the result type of a function/type of a variable/element type of a subscript, which cannot be changed without affecting API resilience. We could allow an API originally specified using an opaque result type to later evolve to specify the specific result type. The result type itself would have to become visible to clients, and this might affect source compatibility, but (mangled name aside) such a change would be resilient.However, the underlying concrete type can change from one version to the next without breaking ABI, because that type is not known to clients of the API. 真の型を変えても ABI が変わらないってことはー、コンパイル時に解決するというのと矛盾しているような。熟読しないとわからないでござる。ABI、 APIのタイポじゃなかろうか・・・var a = f1() a = f2() が同一モジュール内はコンパイルエラーだけど、モジュールまたいだらコンパイル通るということになりませんか?func foo<C: Collection>(_ : C) { } foo(c) // okay: unlike existentials, opaque types work with genericsextension BidirectionalCollection { public func reversed() -> opaque BidirectionalCollection where _.Element == Element where Self: RandomAccessCollection -> _: RandomAccessCollection where Self: MutableCollection -> _: MutableCollection { return ReversedCollection<Self>(...) } }struct ReversedCollection<C: BidirectionalCollection>: BidirectionalCollection {...} extension ReversedCollection: RandomAccessCollection where C: RandomAccessCollection {}extension BidirectionalCollection { public func reversed() -> opaque BidirectionalCollection where _.Element == Element { return ReversedCollection<Self>(...) } } (edited)extension RandomAccessCollection { public func reversed() -> opaque RandomAccessCollection where _.Element == Element { return ReversedCollection<Self>(...) } }func h() -> opaque Comparable { return /* ... */ } var hArray = [h(), h(), h()] hArray.sort() // okay! the Element type is Comparable, and all types are the sameIt is not a plan for Swift 3, nor an official core team communication@dynamicCallable でもずいぶん昔に通ったイメージ。// $ swift -swift-version 5 protocol CopyInitializable { init(copy: Self) } extension CopyInitializable { init(copy: Self) { self = copy } } class Animal : CopyInitializable { convenience init(animal: Animal) { self.init(copy: animal) } }a.swift:13:25: error: cannot convert value of type 'Animal' to expected argument type 'Self' self.init(copy: animal) ^~~~~~ as! Self> おかしいのは、ここで self.init(_with: ) が呼び出せてしまうことじゃないですか? > AnimalProtocolが引数に受けてる Self は、 AnimalBase が final で無いのだから > 何かのサブクラスである可能性があって (たとえばCat ) > なのに、 obj: AnimalBase を渡せてしまっているNoError 要らずでいい感じ。 enum Result<Value, Error: Swift.Error> { case success(Value) case failure(Error) func get() throws -> Value { switch self { case .success(let value): return value case .failure(let error): throw error } } } extension Result where Error == Never { func get() -> Value { switch self { case .success(let value): return value } } } let a: Result<Int, Never> = .success(42) print(a.get())enum Result<Value, Error: Swift.Error> { case success(Value) case failure(Error) func get() throws -> Value { switch self { case .success(let value): return value case .failure(let error): throw error } } } extension Result where Error == Never { func get() -> Value { switch self { case .success(let value): return value } } } let a: Result<Int, Never> = .success(42) print(a.get())<stdin>:15:30: error: same-type constraint type 'Never' does not conform to required protocol 'Error' extension Result where Error == Never { ^ <stdin>:24:8: error: type 'Never' does not conform to protocol 'Error' let a: Result<Int, Never> = .success(42) ^ <stdin>:17:9: error: switch must be exhaustive switch self { ^ <stdin>:17:9: note: add missing case: '.failure(_)' switch self { ^let a: Int = fatalError()<stdin>:1:14: error: cannot convert value of type 'Never' to specified type 'Int' let a: Int = fatalError() ^~~~~~~~~~~~protocol Animal {} func foo<A: Animal>(_ a: @autoclosure () -> A) {} foo(fatalError())<stdin>:3:1: error: global function 'foo' requires that 'Never' conform to 'Animal' foo(fatalError()) ^ <stdin>:2:6: note: where 'A' = 'Never' func foo<A: Animal>(_ a: @autoclosure () -> A) {} ^func f<E: Error>(_ e: E) {} f(fatalError()) (edited)<stdin>:2:3: warning: will never be executed f(fatalError()) ^ <stdin>:2:3: note: a call to a never-returning function f(fatalError()) ^ Fatal error: : file <stdin>, line 2 Current stack trace: 0 libswiftCore.so 0x00007f9510c6da50 _swift_stdlib_reportFatalErrorInFile + 115 1 libswiftCore.so 0x00007f9510bad167 <unavailable> + 3617127 2 libswiftCore.so 0x00007f95109c0785 <unavailable> + 1599365 3 libswiftCore.so 0x00007f9510bacf92 <unavailable> + 3616658 4 libswiftCore.so 0x00007f95109bf9bd <unavailable> + 1595837 5 libswiftCore.so 0x00007f9510b6581f <unavailable> + 3323935 6 libswiftCore.so 0x00007f95109bf149 <unavailable> + 1593673 8 swift 0x00000000010b39de <unavailable> + 13318622 9 swift 0x00000000010b7a62 <unavailable> + 13335138 10 swift 0x0000000000519d85 <unavailable> + 1154437 11 swift 0x00000000004ee6e5 <unavailable> + 976613 12 swift 0x00000000004e9aa1 <unavailable> + 957089 13 swift 0x0000000000494fa0 <unavailable> + 610208 14 libc.so.6 0x00007f9513136740 __libc_start_main + 240 15 swift 0x0000000000492dd9 <unavailable> + 601561 Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -swift-version 5 -module-name main #0 0x00000000044580a4 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x44580a4) #1 0x0000000004455f70 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4455f70) #2 0x0000000004458252 SignalHandler(int) (/usr/bin/swift+0x4458252) #3 0x00007f9514a0c390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007f9510b65823 $Ss17_assertionFailure__4file4line5fla (edited)func f<X: Error>(_ error: X) {} f(fatalError())<stdin>:2:3: warning: will never be executed f(fatalError()) ^ <stdin>:2:3: note: a call to a never-returning function f(fatalError()) ^ Fatal error: : file <stdin>, line 2 Current stack trace: 0 libswiftCore.so 0x00007f6cf4244a50 _swift_stdlib_reportFatalErrorInFile + 115 1 libswiftCore.so 0x00007f6cf4184167 <unavailable> + 3617127 2 libswiftCore.so 0x00007f6cf3f97785 <unavailable> + 1599365 3 libswiftCore.so 0x00007f6cf4183f92 <unavailable> + 3616658 4 libswiftCore.so 0x00007f6cf3f969bd <unavailable> + 1595837 5 libswiftCore.so 0x00007f6cf413c81f <unavailable> + 3323935 6 libswiftCore.so 0x00007f6cf3f96149 <unavailable> + 1593673 8 swift 0x00000000010b39de <unavailable> + 13318622 9 swift 0x00000000010b7a62 <unavailable> + 13335138 10 swift 0x0000000000519d85 <unavailable> + 1154437 11 swift 0x00000000004ee6e5 <unavailable> + 976613 12 swift 0x00000000004e9aa1 <unavailable> + 957089 13 swift 0x0000000000494fa0 <unavailable> + 610208 14 libc.so.6 0x00007f6cf670d740 __libc_start_main + 240 15 swift 0x0000000000492dd9 <unavailable> + 601561 Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -swift-version 5 -module-name main #0 0x00000000044580a4 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x44580a4) #1 0x0000000004455f70 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4455f70) #2 0x0000000004458252 SignalHandler(int) (/usr/bin/swift+0x4458252) #3 0x00007f6cf7fe3390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007f6cf413c823 $Ss17_assertionFailure__4file4line5flafunc f<X: Error>(_ error: @autoclosure ()->X) {} f(fatalError())Never は Error や Equatable になっただけで、 bottom になったわけではないはず。ただ、たしか Chris Lattner か Joe Groff が forum の議論の最初の方で bottom type に言及してた気が。Equatable 入れるなら Error も入れるのがいいんじゃない?みたいな会話があったと思う。Chris Lattner Jun 13 Moderate -1. Never is special because it has no instances - it is uninhabited by design. This means that it can conform to all protocols. I'd rather see a global solution rather than cherry picking these two specific protocols.
Joe_Groff Jun 13 If we're cherry-picking protocols to conform Never to, Error seems like another good one to whitelist, since it's useful to use Never to close off the error path in Result<T, Never>-like abstractions.
https://forums.swift.org/t/se-0215-conform-never-to-equatable-and-hashable/13586 (edited)let range: Range<Float> = 0..<1 let x = range.randomElement()<stdin>:2:10: error: ambiguous reference to member 'randomElement(using:)' let x = range.randomElement() ^~~~~ Swift.Collection:11:17: note: found this candidate public func randomElement<T>(using generator: inout T) -> Self.Element? where T : RandomNumberGenerator ^ Swift.Collection:12:17: note: found this candidate public func randomElement() -> Self.Element? ^Range<Float> は Collection じゃないので、実際には randomElement() 生えていないということだと思います。エラーメッセージも悪いし、Xcode で補完されちゃうのも悪い。var a = [2, 3, 5] var b = a[...] a.removeFirst() print(a.startIndex) b.removeFirst() print(b.startIndex)ArraySlice の popFirst は startIndex を動かすけど、 removeFirst は動かさずに前に詰める(というか、先頭ポインタを動かして startIndex は動かさない)のかと思ってた。removeFirstとpopFirstで返り値型が違うんですねArray と ArraySlice の同じメソッドで startIndex が動くか動かないかという挙動が違う件ですね。startIndex が動かないようにも実装できるはず。ArraySliceのremoveFirstの実装がどれなのか確定できてないですが上位プロトコルで実装されててArraySlice以外の事情も絡んでそうな気がしてます。 https://github.com/apple/swift/blob/ae6f5dd60460f340d98cf24c402c6489bb5a4f2b/stdlib/public/core/Collection.swift#L1765-L1781func foo<C: RangeReplaceableCollection>(_ collection: C) -> C.Index? { var collection = collection if collection.isEmpty { return nil} collection.removeFirst() return collection.startIndex } var a: Array<Int> = [2, 3, 5] var b: ArraySlice<Int> = a[...] print(foo(a)!) print(foo(b)!)Collection の Index は opaque として扱われるべきで、実値は気にするべきではないと思います。Array と ArraySlice は直接関係があるわけでないしいいのかな。== と = が並んで読みづらい・・・。 var strings: opaque Collection where _.Element == String = ["hello", "world"]let strings: Any<Sequence where .Iterator.Element == String> = ["a", "b", "c"] だから、書き換えたら↓みたいな感じ? let strings: Sequence where _.Iterator.Element == String = ["a", "b", "c"]opaque がなければ Generalized existential になるのか。Foo と Bar があって、両方に Cat という型があって、 Bar モジュールに Bar という型がある場合に、 Foo の中から Bar.Cat を表現するにはどうすればいいですか?↓のようにすると Bar.Bar.Cat を意味してしまってエラーになってしまいました。 // Foo モジュールにて import Bar struct Cat { init(_ cat: Bar.Cat) { ... } } (edited)import Bar as B とかできればいいんですが・・・。Result は パッケージ名変えて欲しい。Fooのソースの中で必要とするのがBar.Catだけであれば、 import struct Bar.Cat で解決できるかも。import struct Bar.Cat typealias BarCat = Bar.Cat として BarCat としてアクセスできました。難点は、 BarCat を使う API が public な場合、 typealias を露出させないといけないこと。typealias AntiResult<T, E: Error> = Result<T, E> とかでも良さそうな気も。typealias 露出しないでもいけるかな・・・ (edited)Result だと無理そう・・・typealias 以前に、 norio さんが挙げてくれた方法も自モジュールで Result 型を宣言してたら無理そう。 (edited)Alamofire で宣言されたものとの alias だから良いだけで。Foo.Cat と Bar.Cat と Bar.Bar の問題だから回避できたけど、Foo.Bar と Bar.Bar を Foo の中で解決するのはきつそう。1 Cat にならないのってなんででしょう? 1 Animal の方のメソッドがジェネリックでないから? @swift-4.2.4
class Animal<T> {} class Cat<T, U>: Animal<T> {} struct Hoge { func foo(_: Animal<Hoge>) { print("1 Animal") } func foo<U>(_: Cat<Hoge, U>) { print("1 Cat") } } Hoge().foo(Cat<Hoge, Int>()) // 1 Animal func foo<T>(_: Animal<T>) { print("2 Animal") } func foo<T, U>(_: Cat<T, U>) { print("2 Cat") } foo(Cat<Hoge, Int>()) // 2 Cat (edited)PatialKeyPath と WritableKeyPath でこのパターンでオーバーロードして死んだ。Comparing 2 viable solutions --- Solution #0 --- Fixed score: 0 0 0 0 0 0 0 0 0 0 0 ... Constraint restrictions: Cat<Hoge, Int> to Animal<Hoge> is [superclass] Cat<Hoge, Int> to Cat<Hoge, Int> is [deep equality] ... --- Solution #1 --- Fixed score: 0 0 0 0 0 0 0 0 0 0 0 ... Constraint restrictions: Cat<Hoge, Int> to Cat<Hoge, Int> is [deep equality] ...comparing solutions 1 and 0 Comparing declarations func ffoo<U>(_: Cat<Hoge, U>) { } and func ffoo(_: Animal<Hoge>) { } comparison result: not better Comparing declarations func ffoo(_: Animal<Hoge>) { } and func ffoo<U>(_: Cat<Hoge, U>) { } comparison result: betterswift -Xfrontend -debug-constraints a.swift Comparing declarations func foo<T>(_: Animal<T>) { } and func foo<T, U>(_: Cat<T, U>) { } comparison result: not better Comparing declarations func foo<T, U>(_: Cat<T, U>) { } and func foo<T>(_: Animal<T>) { } (found solution 0 0 0 0 0 0 0 0 0 0 0) comparison result: better (edited)open class Animal<T> class Cat<T, U>: Animal<T>() class Hoge { fun foo(a: Animal<Hoge>) { println("1 Animal") } fun <U> foo(a: Cat<Hoge, U>) { println("1 Cat")} } fun <T> foo(a: Animal<T>) { println("2 Animal") } fun <T, U> foo(a: Cat<T, U>) { println("2 Cat") } fun main(args: Array<String>) { Hoge().foo(Cat<Hoge, Int>()) // 1 Cat foo(Cat<Hoge, Int>()) // 2 Cat } (edited)1 Cat が自然だと思うんだけどなぁ。PartialKeyPath<T> と、そのサブクラスである KeyPath<T, U> や WritableKeyPath<T, U> でオーバーロードして、 T に Self とか入れたときに後者が勝つ方法がなさそう。U を Any にしてしまえば勝てるだろうけど・・・。 (edited)Any にしても variance がうまく働かない?let a: KeyPath<[Int], Int> = \[Int].count let b: KeyPath<[Int], Any> = a<stdin>:2:30: error: cannot convert value of type 'KeyPath<[Int], Int>' to specified type 'KeyPath<[Int], Any>' let b: KeyPath<[Int], Any> = a ^ // A concrete type is better than an archetype. // FIXME: Total hack. if (type1->is<ArchetypeType>() != type2->is<ArchetypeType>()) { if (type1->is<ArchetypeType>()) ++score2; else ++score1; continue; }class Animal {} class Cat<T>: Animal {} func foo(_: Animal) { print("Animal") } func foo<T>(_: Cat<T>) { print("Cat") } foo(Cat<Int>())class Animal { func fooImpl() { print("Animal") } } class Cat<T>: Animal { override func fooImpl() { print("Cat") } } func foo(_ animal: Animal) { animal.fooImpl() } func foo<T>(_ cat: Cat<T>) { cat.fooImpl() } foo(Cat<Int>())func foo(_ animal: Animal) func foo<T>(_ cat: Cat<T>)foo<T> って完全に腐ってる?class Animal {} class Cat<T>: Animal {} func foo(_: Animal) { print("Animal") } func foo<T>(_: Cat<T>) { print("Cat") } let f: (Cat<Int>) -> Void = foo f(Cat<Int>())Animal 優先は辛い・・・class Animal {} class Cat<T>: Animal {} func foo<X>(_: X, _: Animal) { print("Animal") } func foo<X, T>(_: X, _: Cat<T>) { print("Cat") } foo((), Cat<Int>())class Animal {} class Cat<T>: Animal {} func foo<T: Animal>(_: T) { print("Animal") } func foo<T>(_: Cat<T>) { print("Cat") } foo(Cat<Int>())Never が bottom でも無理?Animal と Cat<T> だと Cat<T> を優先してほしいのが自然な感覚な気がするんですが、継承で型パラを足すというのがレアケースなので考慮漏れの可能性もあるかなと思うんですが。確か、初期の Swift は継承で型パラ足すのできなかったですよね?Animal が protocol の場合も同結果になるのは結構きついと思った。 (edited)class A {} protocol BProtocol { associatedtype Info: A var info: Info? { get set } } class B<T1: A>: BProtocol { var info: T1? } protocol CProtocol { associatedtype Property: BProtocol var property: Property? { get set } } class C<T2>: CProtocol where T2 : BProtocol { var property: T2? }<T2: B<A>> で二重になってたのが悪かったんですかね、やっぱり。class A {} protocol BProtocol { associatedtype Info: A var info: Info? { get set } } class B<T1: A>: BProtocol { var info: T1? } protocol CProtocol { associatedtype Property: BProtocol var property: Property? { get set } } // vvvvvvvv ここが違う class C<T2 : BProtocol>: CProtocol { var property: T2? } (edited)<T2: BProtocol> にしても当初表現したかった形としては成立してるので、その形に修正しようかな。。ありがとうございます。最新 master ではコンパイル通ったので放っておいても直る Swift 5っていつ頃リリース予定でしたっけ?Optional を flatten することが引き起こすちょっと微妙な(おもしろい?)例。 @swift-4.2.4
func foo(_ x: [Int?]?) -> Any { return x?[0] as Any } func bar<T>(_ x: [T]?) -> Any { return x?[0] as Any } let a: [Int?]? = [2, 3, 5] print(String(describing: foo(a))) print(String(describing: bar(a))) (edited)T に Int? を埋めたら同じコードに見えるけど結果が異なる。enum Sample<T> { case Foo(T) case Bar(T) } indirect enum Sample1<T> { case Foo(T) case Bar(T) } enum Sample2<T> { indirect case Foo(T) case Bar(T) } enum Sample3<T> { indirect case Foo(T) indirect case Bar(T) }`enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) } let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) print(sum) print(product)`addition(main.ArithmeticExpression.number(5), main.ArithmeticExpression.number(4)) multiplication(main.ArithmeticExpression.addition(main.ArithmeticExpression.number(5), main.ArithmeticExpression.number(4)), main.ArithmeticExpression.number(2))enum ArithmeticExpression { case number(Int) indirect case addition(ArithmeticExpression, ArithmeticExpression) indirect case multiplication(ArithmeticExpression, ArithmeticExpression) } let five = ArithmeticExpression.number(5) let four = ArithmeticExpression.number(4) let sum = ArithmeticExpression.addition(five, four) let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) func evaluate(_ expression: ArithmeticExpression) -> Int { switch expression { case let .number(value): return value case let .addition(left, right): return evaluate(left) + evaluate(right) case let .multiplication(left, right): return evaluate(left) * evaluate(right) } } print(evaluate(product))`class A<E> { convenience init<X>(_ array: [X]) where X : Equatable, X == E { self.init(array, { (a, b) in a == b }) } init(_ array: [E], _ eq: @escaping (E, E) -> Bool) {} }
struct KeySortedArray<Element, Key : Comparable> { init(_ array: [Element], _ getKey: @escaping (Element) -> Key) {} } extension KeySortedArray where Element : Comparable, Element == Key { init(_ array: [Element]) { } } class Cat { init() { KeySortedArray(self.blocks, { $0.time }) } }#0 0x000000000410b054 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x410b054) #1 0x0000000004108ee2 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4108ee2) #2 0x000000000410b202 SignalHandler(int) (/usr/bin/swift+0x410b202) #3 0x00007f0b420d2390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00000000016b1b09 swift::FunctionType::get(swift::Type, swift::Type, swift::AnyFunctionType::ExtInfo const&) (/usr/bin/swift+0x16b1b09) #5 0x00000000017fde74 swift::GenericFunctionType::substGenericArgs(swift::SubstitutionMap const&) (/usr/bin/swift+0x17fde74) #6 0x00000000017fdd02 swift::GenericFunctionType::substGenericArgs(llvm::ArrayRef<swift::Substitution>) (/usr/bin/swift+0x17fdd02) #7 0x000000000147abe0 swift::UncurriedCandidate::UncurriedCandidate(swift::ValueDecl*, unsigned int) (/usr/bin/swift+0x147abe0) #8 0x000000000147da0f swift::CalleeCandidateInfo::collectCalleeCandidates(swift::Expr*, bool) (/usr/bin/swift+0x147da0f) #9 0x00000000014667e1 (anonymous namespace)::FailureDiagnosis::visitApplyExpr(swift::ApplyExpr*) (/usr/bin/swift+0x14667e1) #10 0x000000000144d4d6 swift::ASTVisitor<(anonymous namespace)::FailureDiagnosis, bool, void, void, void, void, void>::visit(swift::Expr*) (/usr/bin/swift+0x144d4d6) #11 0x0000000001446642 swift::constraints::ConstraintSystem::diagnoseFailureForExpr(swift::Expr*) (/usr/bin/swift+0x1446642) #12 0x000000000144cb76 swift::constraints::ConstraintSystem::salvage(llvm::SmallVectorImpl<swift::constraints::Solution>&, swift::Expr*) (/usr/bin/swift+0x144cb76) #13 0x0000000001352138 swift::TypeChecker::solveForExpression(swift::Expr*&, swift::DeclContext*, swift::Type, swift::FreeTypeVariableBinding, swift::ExprTypeCheckListener*, swift::constraints::ConstraintSystem&, llvm::SmallVectorImpl<swift::constraints::Solution>&, swift::OptionSet<swift::TypeCheckExprFlags, unsigned int>) (/usr/bin/swift+0x1352138) #14 0x0000000001356465 swift::TypeChecker::typeCheckExpression(swift::Exlet display.Name: String = "hola" print(display.Name)`<stdin>:2:12: error: consecutive statements on a line must be separated by ';' let display.Name: String = "hola" ^ ; <stdin>:2:17: error: consecutive statements on a line must be separated by ';' let display.Name: String = "hola" ^ ; <stdin>:2:17: error: expected expression let display.Name: String = "hola" ^ <stdin>:2:5: error: type annotation missing in pattern let display.Name: String = "hola" ^ <stdin>:2:13: error: reference to member 'Name' cannot be resolved without a contextual type let display.Name: String = "hola" ~^~~~func writeData() throws { // ここにclose失敗のthrowsが必要, スコープ脱出時だからtry catchはできない let stream = try OutputStream() try stream.write("aaa") } (edited)do で囲んだらちょうどスコープ脱出時になるのでは?
do { let stream = try OutputStream() try stream.write("aaa") } catch ... { // stream が破棄されて deinit の throws も catch }struct Foo { var a: Any func bar(_ out: OutputStream) { a = out } func baz() { do { let stream = try OutputStream() bar(stream) } catch let error { // ここで stream が破棄されるか調べるのが大変 ... } } }deinitをthrowsにするのは無茶な気がする。endScope<T>(T) が入ると明示的に呼び出せる・・・deinit の throws がついてないと子の deinit で throws つけられないし厳しそうObject を継承してるわけじゃないから。Any はクラスじゃないし。でも existential が破綻するのか。deinit throwsが公開されてる型なのかチェックしないといけないとか、無理すぎる。func run(_ body: @escaping () -> Void) { body() } class Foo { var a: Int = 42 func bar() { run { a += 1 } } }<stdin>:9:13: error: reference to property 'a' in closure requires explicit 'self.' to make capture semantics explicit a += 1 ^ self.func run(_ body: @escaping () -> Void) { body() } class Foo { var a: Int = 42 func bar() { func incrementA() { a += 1 } run(incrementA) } }self. いらないんですっけ?仕様?チェック漏れ? { } で書いたときと func で書いた時で self の暗黙キャプチャ挙動が変わるincrementA )について?
(edited)write(to:) )あるの初めて知った。 @swift-4.2.4
var out = "" "abc".write(to: &out) "def".write(to: &out) print(out)+ 演算子と何が違うん?と思いましたが、 to: の先は TextOutputStream なんですね。String は TextOutputStream というのがおもしろいです。String は可変長配列のように末尾に効率的に文字列を追加できるので、文字列を書き出す対象になれるということですね。一方で、(値型なので)他の言語でよくあるイミュータブルな文字列インスタンスと同じような性質も持っていると。String と StringBuilder と Writer を合わせたような感じの役割を果たしてる感じでしょうか。dump(to:) も同じ感じですね。これが TextOutputStream な理由は、(1) 標準出力とかに吐き出す都合で直接吐けた方が楽 or (2) 単にでかいテキストの末尾追加が発生しやすいから、のどちらかだと思うんですが、多分 1 ? https://developer.apple.com/documentation/swift/1641218-dump# (edited)String なことで stream 的な性質を併せ持つこともできたということじゃないでしょうか?できるならそうしたいけど参照型のイミュータブルな String ではできなかったという意味合いで。func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: Int) { print("Int: \(x)") } foo(.max)<stdin>:10:6: error: ambiguous use of 'max' foo(.max) ^ Swift.UnsignedInteger:4:23: note: found this candidate public static var max: Self { get } ^ Swift.SignedInteger:4:23: note: found this candidate public static var max: Self { get } ^func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: Int) { print("Int: \(x)") } foo(.max - 1)Int: 92233720368547758061 が Int と推論されて、それによって .max の型が Int と推論され、 Int.max になってる?.max じゃダメだけど .max - 1 だと OK なのが、おもしろいというかわかりにくいというか・・・func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: UInt16) { print("Int: \(x)") } foo(.max+0)func foo(_ x: UInt8) { print("UInt8: \(x)") } func foo(_ x: UInt16) { print("Int: \(x)") } foo(.max+0)swift-4.1.1-RELEASE/usercode/main.swift:9:9: error: ambiguous use of operator '+' foo(.max+0) ^ Swift.UInt8:9:24: note: found this candidate public static func + (lhs: UInt8, rhs: UInt8) -> UInt8 ^ Swift.UInt16:9:24: note: found this candidate public static func + (lhs: UInt16, rhs: UInt16) -> UInt16 ^ExpressibleBy...Literal があるから任意の型に任意のリテラルを設定できるけど、何も指定しなかった場合のデフォルトは決まってて、その関係で 1 の型が決定されて .max の型も推論されてると思われる。let a = .max - 1 print(a) let b: UInt8 = .max - 1 print(b)9223372036854775806 254let a = .max<stdin>:2:10: error: reference to member 'max' cannot be resolved without a contextual type let a = .max ~^~~String ?標準出力?func foo(_ x: UInt8) {} func foo(_ x: Int) {} foo(.max - 1)---Constraint solving for the expression at [<stdin>:6:1 - line:6:13]--- ---Initial constraints for the given expression--- (call_expr type='()' location=<stdin>:6:1 range=[<stdin>:6:1 - line:6:13] arg_labels=_: (overloaded_decl_ref_expr type='$T0' location=<stdin>:6:1 range=[<stdin>:6:1 - line:6:1] name=foo number_of_decls=2 function_ref=single decls=[ main.(file).foo@<stdin>:2:6, main.(file).foo@<stdin>:4:6]) (paren_expr type='($T6)' location=<stdin>:6:10 range=[<stdin>:6:4 - line:6:13] (binary_expr type='$T6' location=<stdin>:6:10 range=[<stdin>:6:5 - line:6:12] (overloaded_decl_ref_expr type='$T1' location=<stdin>:6:10 range=[<stdin>:6:10 - line:6:10] name=- number_of_decls=20 function_ref=unapplied decls=[ Swift.(file).Float.-, Swift.(file).Double.-, Swift.(file).Float80.-, Swift.(file).UInt8.-, Swift.(file).Int8.-, Swift.(file).UInt16.-, Swift.(file).Int16.-, Swift.(file).UInt32.-, Swift.(file).Int32.-, Swift.(file).UInt64.-, Swift.(file).Int64.-, Swift.(file).UInt.-, Swift.(file).Int.-, Swift.(file).FloatingPoint.-, Swift.(file).Numeric.-, Swift.(file).BinaryInteger.-, Swift.(file).Strideable.-, Swift.(file).Strideable.-, Swift.(file).Strideable.-, Swift.(file).Strideable.-]) (tuple_expr implicit type='($T4, $T5)' location=<stdin>:6:5 range=[<stdin>:6:5 - line:6:12] (unresolved_member_expr type='$T4' location=<stdin>:6:6 range=[<stdin>:6:5 - line:6:6] name='max' arg_labels=') (integer_literal_expr type='$T5' location=<stdin>:6:12 range=[<stdin>:6:12 - line:6:12] value=1))))) Score: 0 0 0 0 0 0 0 0 0 0 0 Type Variables: $T0 [lvalue allowed] subtype_of_existential involves_type_vars bindings={} @ locator@0x68fcd10 [OverloadedDeclRef@<stdin>:6:1] $T1 [lvalue allowed] subtype_of_existential involves_type_vars bindings={} @ locator@0x68fce60 [Ove--- Solution #0 --- Fixed score: 0 0 0 0 0 1 0 0 0 0 0 Type variables: $T1 as (UInt8, UInt8) -> UInt8 @ locator@0x68fce60 [OverloadedDeclRef@<stdin>:6:10] --- Solution #1 --- Fixed score: 0 0 0 0 0 0 0 0 0 0 0 Type variables: $T1 as (Int, Int) -> Int @ locator@0x68fce60 [OverloadedDeclRef@<stdin>:6:10]enum ScoreKind { // These values are used as indices into a Score value. /// A fix needs to be applied to the source. SK_Fix, /// A reference to an @unavailable declaration. SK_Unavailable, /// An implicit force of an implicitly unwrapped optional value. SK_ForceUnchecked, /// A user-defined conversion. SK_UserConversion, /// A non-trivial function conversion. SK_FunctionConversion, /// A literal expression bound to a non-default literal type. SK_NonDefaultLiteral, /// An implicit upcast conversion between collection types. SK_CollectionUpcastConversion, /// A value-to-optional conversion. SK_ValueToOptional, /// A conversion to an empty existential type ('Any' or '{}'). SK_EmptyExistentialConversion, /// A key path application subscript. SK_KeyPathSubscript, /// A conversion from a string, array, or inout to a pointer. SK_ValueToPointerConversion, SK_LastScoreKind = SK_ValueToPointerConversion, };{ ∃X where X: Animal, X } の事で 、その考え方ではプロトコルに準拠してるのはXであって Existential ではないから、という説明になるらしいですけど、僕はよくわかってないです。
func package<T: P>(_ t: T) -> P { return t } let p0: P = package(Cat()) let p1: P = package(p0) こうすると入れ子になると思います。実装上それを避ける事はできそうですが、変なケースが生じないかとか、入れ子にならないことが正しいのか?とか気になります。 (edited)protocol P { var x: Any { get set } } struct S: P { var x: Any } let p: P = S(x: 42)Any も existential だからすでに埋め込めるのでは?protocol Q {} protocol P { var x: Q { get set } } struct T: Q {} struct S: P { var x: Q } let p: P = S(x: T()) (edited)P には Q が埋め込まれてない?<T> T: P のときに T を P にアップキャストする挙動のことですfunc package<T: P>(_ t: T) -> P { // ここでPのExistentialが作られて、Tはその中に保持される return t } // これは普通 let p0: P = package(Cat()) // このとき、callee側ではPではなくT: Pとして来るので、 // 同じように T を保持する P が作られて、 結果的に P { P { T } } になりそう。 let p1: P = package(p0)protocol P { var x: Q { get set } } ↑ここを見て言いましたt を使おうとした瞬間に open されてもう一度 existential container に包まれるんじゃないのかなぁ。P を T として扱おうとした時点で、暗黙的に open するんじゃない?let p1: P = package(p0 /* あ〜ここか */)func foo<T: P>(_ t: T) { t.foo() }T が P でも動かないといけないわけでopen してから渡すかlet b = Bool.random() switch b { case true: print("T") case false: print("F") }Bool だけ特殊?@dynamicCallable の PR が作り直されていて [WIP] も外れてる。 https://github.com/apple/swift/pull/20305AnyHashable の実装みてたんですが、 associatedtype無しの Self 制約だけであれば protocol で type erasure 書けるんですねー。protocol Equattable { func equaals(to other: Self) -> Bool } extension Int: Equattable { func equaals(to other: Int) -> Bool { return self == other } } extension String: Equattable { func equaals(to other: String) -> Bool { return self == other } } protocol _AnyEquattableBox { func _unbox<T: Equattable>() -> T? func _equaals(to box: _AnyEquattableBox) -> Bool? } struct _ConcreteEquattableBox<Base: Equattable> : _AnyEquattableBox { var _baseEquattable: Base init(_ base: Base) { self._baseEquattable = base } func _unbox<T: Equattable>() -> T? { return (self as _AnyEquattableBox as? _ConcreteEquattableBox<T>)?._baseEquattable } func _equaals(to box: _AnyEquattableBox) -> Bool? { guard let rhs: Base = box._unbox() else { return nil } return _baseEquattable.equaals(to: rhs) } } struct AnyEquattable: Equattable { var _box: _AnyEquattableBox init<E: Equattable>(_ base: E) { _box = _ConcreteEquattableBox(base) } func equaals(to rhs: AnyEquattable) -> Bool { return _box._equaals(to: rhs._box) ?? false } } let intVal1 = AnyEquattable(1) let intVal2 = AnyEquattable(1) let intVal3 = AnyEquattable(2) let strVal = AnyEquattable("test") assert(intVal1.equaals(to: intVal2)) assert(!intVal1.equaals(to: intVal3)) assert(!intVal1.equaals(to: strVal))_canonicalBox というのが何故必要なのか? let a = (42 as Int as AnyHashable) let b = (42 as NSNumber as AnyHashable) let c = (42 as Double as AnyHashable) a == b // true b == c // true a == c // was false(!), now trueprint(AnyHashable(42 as Int) == AnyHashable(42.0 as Double))print(AnyHashable(42 as Int) == AnyHashable(42.0 as Double))protocol AnyEquatableBox { func _unbox<T: Equatable>() -> T? func _equals(to box: AnyEquatableBox) -> Bool? } struct ConcreteEquatableBox<Base: Equatable>: AnyEquatableBox { var base: Base init(_ base: Base) { self.base = base } func _unbox<T>() -> T? where T : Equatable { return ((self as AnyEquatableBox) as? ConcreteEquatableBox<T>)?.base } func _equals(to box: AnyEquatableBox) -> Bool? { guard let rhs: Base = box._unbox() else { return nil } return base == rhs } } struct AnyEquatable: Equatable { var box: AnyEquatableBox init<E>(_ base: E) where E: Equatable { box = ConcreteEquatableBox(base) } static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool { return lhs.box._equals(to: rhs.box) ?? false } } class Animal: Equatable { static func == (lhs: Animal, rhs: Animal) -> Bool { return lhs === rhs } } class Cat: Animal { } let cat1 = Cat() let cat2 = cat1 as Animal assert(AnyEquatable(cat1) == AnyEquatable(cat2))Assertion failed: : file <stdin>, line 46 Current stack trace: 0 libswiftCore.so 0x00007fde1b750fa0 _swift_stdlib_reportFatalErrorInFile + 215 1 libswiftCore.so 0x00007fde1b4a95c1 <unavailable> + 1504705 2 libswiftCore.so 0x00007fde1b6e60c2 <unavailable> + 3850434 3 libswiftCore.so 0x00007fde1b4a8d0a <unavailable> + 1502474 4 libswiftCore.so 0x00007fde1b6e5f5c <unavailable> + 3850076 5 libswiftCore.so 0x00007fde1b4a8d0a <unavailable> + 1502474 6 libswiftCore.so 0x00007fde1b642a28 <unavailable> + 3181096 7 libswiftCore.so 0x00007fde1b4a85a9 <unavailable> + 1500585 9 swift 0x0000000001043efe <unavailable> + 12861182 10 swift 0x0000000001048022 <unavailable> + 12877858 11 swift 0x00000000004f8b42 <unavailable> + 1018690 12 swift 0x00000000004df0bb <unavailable> + 913595 13 swift 0x00000000004da3f0 <unavailable> + 893936 14 swift 0x000000000048a348 <unavailable> + 566088 15 libc.so.6 0x00007fde1e0f5740 __libc_start_main + 240 16 swift 0x0000000000488009 <unavailable> + 557065 #0 0x000000000410ac94 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x410ac94) #1 0x0000000004108b22 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4108b22) #2 0x000000000410ae42 SignalHandler(int) (/usr/bin/swift+0x410ae42) #3 0x00007fde1f9cb390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390) #4 0x00007fde1b642a30 $Ss17_assertionFailure__4file4line5flagss5NeverOs12StaticStringV_SSAHSus6UInt32VtFTf4nxnnn_n (/usr/lib/swift/linux/libswiftCore.so+0x308a30) #5 0x00007fde1b4a85a9 (/usr/lib/swift/linux/libswiftCore.so+0x16e5a9) #6 0x0000print(AnyHashable(42 as Int) == AnyHashable(42.0 as Double))の挙動は意図したものなのね。 https://github.com/apple/swift/pull/17396class Animal : Hashable { static func ==(a: Animal, b: Animal) -> Bool { return a === b } func hash(into hasher: inout Hasher) { hasher.combine(ObjectIdentifier(self)) } } class Cat : Animal {} class Dog : Animal {} let a = Cat() let b: Animal = a let c: Animal = Dog() print(AnyHashable(a) == AnyHashable(c), 3) (edited)AnyHashableを作るときに、どんな_AnyHashableBoxを使うかカスタマイズできる_HasCustomAnyHashableRepresentationってのがあって、元がDoubleでもInt64(exactly:)で表現可能なら、_IntegerAnyHashableBoxを使う仕組みになってる。 https://github.com/apple/swift/blob/master/stdlib/public/core/FloatingPointTypes.swift.gyb#L1820-L1831AnyHashable(42 as Int) == AnyHashable(42.0 as Double)がtrueになる。_AnyHashableBoxがinternalだから、stdlib外の型ではカスタマイズ出来ないな。protocol AnyEquatableBox { func _unbox<T: Equatable>() -> T? func _equals(to box: AnyEquatableBox) -> Bool? } struct ConcreteEquatableBox<Base: Equatable>: AnyEquatableBox { var base: Base init(_ base: Base) { self.base = base } func _unbox<T: Equatable>() -> T? { return self.base as? T } func _equals(to box: AnyEquatableBox) -> Bool? { guard let rhs: Base = box._unbox() else { return nil } return base == rhs } } struct AnyEquatable: Equatable { var box: AnyEquatableBox init<E>(_ base: E) where E: Equatable { box = ConcreteEquatableBox(base) } static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool { return lhs.box._equals(to: rhs.box) ?? false } } class Animal: Equatable { static func == (lhs: Animal, rhs: Animal) -> Bool { return lhs === rhs } } class Cat : Animal {} class Dog : Animal {} let a = Cat() let b: Animal = a let c: Animal = Dog() assert(AnyEquatable(a) == AnyEquatable(b)) assert(AnyEquatable(a) != AnyEquatable(c)) assert(AnyEquatable(b) == AnyEquatable(a)) assert(AnyEquatable(b) != AnyEquatable(c)) assert(AnyEquatable(c) != AnyEquatable(a)) assert(AnyEquatable(c) != AnyEquatable(b))_unbox を シンブルな conditional cast に変えただけ。_ObjectiveCBridgeableの影響を受けそう。JSONDecoder.decode とかは throws じゃなくて -> Result<...> であるべきということになりそうだけどどうだろう? throws で扱うのに対して、 JSON のデコードの失敗はもっと確定的な処理で、 "an regular return value with a more specific failure type" に適しているように感じる。一種の Simple Domain Error になる?Result が追加されると、既存 API を変更するか、もしくは言語の期待する使い分けと異なるデザインの API が放置されるかという状況になって、どっちになっても結構辛そうな気がする。String(data:using:) とかも Result を return すべきってことになるよね。throws じゃなくて Result になったらずいぶん使いづらいと思う。 https://developer.apple.com/documentation/swift/keyeddecodingcontainer/2921334-decode (edited)String(data:using:) の例も別のメソッド/イニシャライザでOKthrows を使うべきと言っていて0. を付けた感じだと思うんですよね。0.4.2 みたいな。1.0 にした段階で 1.x の間は後方互換を保ち、そのスパンが 5 年くらいがいいのかなと。async/await や actor, typed throws など)が片付くまではそういう形を続けて、それらが片付いたらもう少しきっちり互換性を守るべきなのかなぁと思います。throws は "systemic error" に用いるべきというのもちょっと懐疑的で、↓のようになるのが望ましいと思えない・・・。結局 Result → throws のための get だらけ。 extension Foo: Decodable { init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.a = try container.decode(Int.self, forKey: .a).get() self.b = try container.decode(String.self, forKey: .b).get() } }init は Result を return できないから throws にせざるを得ない。init? のようなものを導入するなら、昔僕が提案してたみたいな | を導入して、 T|E を Result<T, E> のシュガーにした上で、 init| とか導入しないと・・・。init(from decoder: ) throws(JSONError) とか。Result を return の両方を扱えないよ。init が throw する場合と Result を return する場合の両方を扱えないといけないはず。String.init(data:using:) は Result を return したいけど、 Database.init(path:) は throw したいはず。Result が必要なのって、 async/await に対する Future みたいなもので、局所的にしかたなく使うものであって、 API に現れる種類のものじゃないと思うんよね。init じゃなくて Result を return するメソッドとすると↓になる。 extension Foo: Decodable { static func from(_ decoder: Decoder) -> Result<Foo, DecodeError> { return Result { let container = try decoder.container(keyedBy: CodingKeys.self) let a = try container.decode(Int.self, forKey: .a).get() let b = try container.decode(String.self, forKey: .b).get() return Foo(a: a, b: b) } } }get で throws への変換を挟まないともっと悲惨になる。extension Foo: Decodable { static func from(_ decoder: Decoder) -> Result<Foo, DecodeError> { let container = try decoder.container(keyedBy: CodingKeys.self) let a: A switch container.decode(Int.self, forKey: .a) { case .success(let value): a = value case .failure(let error): return .failure(error) } let b: B switch container.decode(Int.self, forKey: .b) { case .success(let value): b = value case .failure(let error): return .failure(error) } return .success(Foo(a: a, b: b)) } } (edited)decode を Result を return するようにしても何がうれしいのかさっぱりだし、 decode を throws のままにするならどこで Result 使うの?って感じ。extension Foo: Decodable { static func from(_ decoder: Decoder) -> Result<Foo, DecodeError> { return decoder.container(keyedBy: CodingKeys.self).flatMap { container in return curry(Foo.init) <^> container.decode(Int.self, forKey: .a) <*> container.decode(String.self, forKey: .b) } } }Result の利用を推奨してる? I want to clarify that I favor adding Result only to address situations where manual propagation is necessary. Manual propagation will always occasionally be useful; adding async/await (or whatever we end up doing) will surely eliminate most of the most common situations, but they'll still exist, e.g. when using futures (which are the equivalent of manual propagation for async/await and accordingly trigger similar requirements). I will continue to recommend against using Result as an alternative mechanism for ordinary propagation of errors, although I have no doubt that some people will insist on doing so; and I continue to believe that using a different error type than Error is generally misguided for the same reason it's generally misguided with ordinary error-handling. I also worry that the ergonomics of this type are significantly hurt by the attempt to support typed errors and (especially) non-Error-conforming errors.
https://forums.swift.org/t/se-0235-add-result-to-the-standard-library/17752/129-public enum Result<Value, Error> { - case success(Value), failure(Error) +public enum Result<Value, Error: Swift.Error> { + case value(Value), error(Error) } (edited)Error: Swift.Error になったのもいいと思う。Result 乱用は絶対に起こると思うけど、 Result 追加するなら考えられる限りほぼベストな実装だと思う。JSONDecoder.decode(_:from:) は Result を return するのがいいの?それともしないのがいいの?って聞いてみた。 https://forums.swift.org/t/revised-se-0235-add-result-to-the-standard-library/18371/26@_frozen public enum Result<Value, Error: Swift.Error> { case value(Value) case error(Error) public func map<NewValue>( _ transform: (Value) -> NewValue ) -> Result<NewValue, Error> public func mapError<NewError>( _ transform: (Error) -> NewError ) -> Result<Value, NewError> public func flatMap<NewValue>( _ transform: (Value) -> Result<NewValue, Error> ) -> Result<NewValue, Error> public func flatMapError<NewError>( _ transform: (Error) -> Result<Value, NewError> ) -> Result<Value, NewError> public func unwrapped() throws -> Value } extension Result where Error == Swift.Error { @_transparent public init(catching body: () throws -> Value) } extension Result : Equatable where Value : Equatable, Error : Equatable { } extension Result : Hashable where Value : Hashable, Error : Hashable { }Error: Swift:Error じゃできないことだった気がするからError の場合でも URLResponse を持ちたかったのか。URLSession.shared.dataTask(with: url) { (result: Result<(URLResponse, Data), (Error, URLResponse?)>) in // Type added for illustration purposes. switch result { case .success(let response): handleResponse(response.0, data: response.1) case .failure(let error): handleError(error) } }Error 型のプロパティでやるのが順当だろうけど、元が Obj-C で NSError だからなぁ。URLResponse をすでに持ってる型なの?URLRespose を持ってないのであれば解決しない気が。extension URLError { /// The URL which caused a load to fail. public var failingURL: URL? { get } /// The string for the URL which caused a load to fail. public var failureURLString: String? { get } /// The state of a failed SSL handshake. public var failureURLPeerTrust: SecTrust? { get } }(result, response) を受け取るんでいいのでは?URLResponse を Optional にしたくないのか。Optional だけどData が存在するな気もするな。URLResponse がエラーケースで失われてるのか・・・value, error でもいいんじゃないかなって感じ。Result をいつ使うべきかが示されないと throws との使い分けで混乱を生みそう。JSONDecoder.decode は throws のままでいいって考えなのかなぁ?async/await で解決できる非同期処理だと思うんよね。
Result を使うのはいいとしてdataTask は URLSessionDataTask を return してるから async/await にできない。URLSessionDataTask に func completion() async throws -> (Data, URLResponse) というメソッドを追加するのがいいと思う。struct URLSessionDataTaskError : Error { var response: URLResponse? var error: Error } (Result<(Data, URLResponse), URLSessionDataTaskError>) -> VoidURLSessionDataTask に func completion() async throws URLSessionDataTaskError -> (Data, URLResponse) (edited)URLSessionDataTask が enum で色んな case を持ってて// vvvvvvv ここが気に食わない struct URLSessionDataTaskError : Error {URLResponse を持つ場合だけその case が response を保持するのがいいんじゃない?enum URLSessionDataTaskError: Error { case ... case ...(URLResponse) ... }URLSessionDataTask がエラーになりうる様々な caseURLSessionDataTaskError 導入してる時点で Foundation との整合性を考える必要性はないのでは。completion の話だけ書き込んでおくか。.dataCorrupted にマッチしてないっぽい。 let jsonData: Data = "".data(using: .utf8)! let result = Result<[Int], Error> { try JSONDecoder().decode([Int].self, from: jsonData) } switch result { case .value(let value): XCTFail("\(value)") case .error(DecodingError.dataCorrupted(let context)): XCTAssertTrue(context.codingPath.isEmpty) case .error(let error): XCTFail("\(error)") }
https://travis-ci.org/koher/SwiftResult/jobs/461127891Result 、 covariance はなくていいんだろうか。特に Result<Int, FooError> を Result<Int, Error> に代入できないの辛そう。b = a.mapError { $0 as Error } ですね。a.flatMap { Result { try foo() }.mapError { $0 as Error } } となって辛そう。a.flatMap { Result { try foo() }.mapError { $0 as! FooError } }as! でダウンキャストResult<Foo, Error> を使ってれば問題ないか。Error の self-conform で特別扱いするなら、 Result<Value, Error: Swift.Error = Swift.Error> も認めて Result<Foo> って書けるようにしてほしい。Result にだけデフォルト型パラを認めてくれたら便利そう。 (edited)Result で第二型パラがなかったら Swift.Error と解釈するとか。import Foundation @objc protocol Animal {} func take<X: Animal>(_ a: X) {} func outer(_ a: Animal) { take(a) }@objcだとself-conformしてる・・・DecodingError ではなく NSError になるっぽい・・・。 import Foundation do { _ = try JSONDecoder().decode([Int].self, from: "".data(using: .utf8)!) } catch let error { print(error) }NSError なのか。import Foundation do { _ = try JSONDecoder().decode([Int].self, from: "".data(using: .utf8)!) } catch let error { print("\(type(of: error)): \(error)") } (edited)DecodingError: ⚠️ [DecodingError.dataCorrupted: The given data was not valid JSON.] (edited)Result のための構文の必要性についても話し合われていたので、将来的に追加されるかもしれませんね。たとえば↓のような構文とかいいかもしれません。 guard let number = result else let error { // error を使うオペレーション } // number を使うオペレーション@_exported import WinSDK // Clang module // WinBase.h public let HANDLE_FLAG_INHERIT: DWORD = 0x00000001 // WinBase.h public let STARTF_USESTDHANDLES: DWORD = 0x00000100 // WinBase.h public let INFINITE: DWORD = DWORD(bitPattern: -1) // WinBase.h public let WAIT_OBJECT_0: DWORD = 0 // minwindef.h public let FALSE: BOOL = 0 // minwindef.h public let TRUE: BOOL = 1protocol P1 { associatedtype Input } protocol P2 { associatedtype Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias Input = T.Input } extension Box: P2 where T: P2 { } func inputType<T>(_ type: T.Type) -> T.Input.Type where T: P2 { fatalError() } struct A: P2, P1 { typealias Input = Int } let _ = inputType(Box<A>.self) (edited)swift: /home/buildnode/jenkins/workspace/oss-swift-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:106: static bool llvm::isa_impl_cl<swift::SugarType, const swift::TypeBase *>::doit(const From *) [To = swift::SugarType, From = const swift::TypeBase *]: Assertion `Val && "isa<> used on a null pointer"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -I /Libraries/.build/x86_64-unknown-linux/debug -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOZlib/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOHTTPParser/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOSHA1/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOAtomics/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIODarwin/include -I /Libraries/.build/checkouts/swift-nio-b68c973e/Sources/CNIOLinux/include -module-cache-path /Libraries/.build/x86_64-unknown-linux/debug/ModuleCache -D DEBUG -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOZlib.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOHTTPParser.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOSHA1.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOAtomics.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIODarwin.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOLinux.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/swift-nio-zlib-support-c4b8a10c/module.modulemap -module-name main -lLibraries #0 0x000000000460a1c4 PrintStackTraceSignalHandler(void*) (/usr/bin/swift+0x460a1c4) #1 0x0000000004607f90 llvm::sys::RunSignalHandlers() (/usr/bin/swift+0x4607f90) #2 0x000000000460a372 SignalHandler(int)protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputType<T>(_ type: T.Type) -> T.Input.Type where T: P2 { fatalError() } struct A: P2, P1 { typealias Input = Int } let _ = inputType(Box<A>.self)protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputType<T>(_ type: T.Type) -> T.Input.Type where T: P2 { fatalError() } struct A: P2, P1 { typealias Input = Int } let _ = inputType(Box<A>.self)swift-DEVELOPMENT-SNAPSHOT-2018-12-15-aFatal error: file /usercode/main.swift, line 28 Current stack trace: 0 libswiftCore.so 0x00007f51ac83c200 _swift_stdlib_reportFatalErrorInFile + 115 1 libswiftCore.so 0x00007f51ac7773fc <unavailable> + 3396604 2 libswiftCore.so 0x00007f51ac7774ee <unavailable> + 3396846 3 libswiftCore.so 0x00007f51ac582f2a <unavailable> + 1347370 4 libswiftCore.so 0x00007f51ac742f42 <unavailable> + 3182402 5 libswiftCore.so 0x00007f51ac5822f9 <unavailable> + 1344249 8 swift 0x0000000000cec7de <unavailable> + 9357278 9 swift 0x0000000000cf09f2 <unavailable> + 9374194 10 swift 0x0000000000515eff <unavailable> + 1138431 11 swift 0x00000000004eae3e <unavailable> + 962110 12 swift 0x00000000004e6712 <unavailable> + 943890 ...protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputTypeP2<T>(_ type: T.Type) -> T.Input.Type where T: P2 { return T.Input.self } func inputTypeP1<T>(_ type: T.Type) -> T.Input.Type where T: P1 { return T.Input.self } struct A: P2, P1 { typealias Input = Int } print(inputTypeP1(Box<A>.self)) // セグフォ print(inputTypeP2(Box<A>.self))protocol P1 { associatedtype Input } extension P1 { typealias InputP1 = Input } protocol P2 { associatedtype Input } extension P2 { typealias InputP2 = Input } struct Box<T> {} extension Box: P1 where T: P1 { typealias InputP1 = T.InputP1 } extension Box: P2 where T: P2 { typealias Input = T.InputP2 } func inputTypeP2<T>(_ type: T.Type) -> T.Input.Type where T: P2 { return T.Input.self } func inputTypeP1<T>(_ type: T.Type) -> T.Input.Type where T: P1 { return T.Input.self } struct A: P2, P1 { typealias Input = Int } print(inputTypeP1(Box<A>.self)) // セグフォ print(inputTypeP2(Box<A>.self))swift-DEVELOPMENT-SNAPSHOT-2018-12-15-aswift: /home/buildnode/jenkins/workspace/oss-swift-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:106: static bool llvm::isa_impl_cl<swift::SugarType, const swift::TypeBase *>::doit(const From *) [To = swift::SugarType, From = const swift::TypeBase *]: Assertion `Val && "isa<> used on a null pointer"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret /usercode/main.swift -disable-objc-interop -module-name main /usr/bin/swift[0x4608a04] /usr/bin/swift[0x46067d0] /usr/bin/swift[0x4608bb2] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7f533b723390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7f5339e62428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7f5339e6402a] /lib/x86_64-linux-gnu/libc.so.6(+0x2dbd7)[0x7f5339e5abd7] /lib/x86_64-linux-gnu/libc.so.6(+0x2dc82)[0x7f5339e5ac82] /usr/bin/swift[0x19464f3] /usr/bin/swift[0xef6b2c] /usr/bin/swift[0xf0af9e] /usr/bin/swift[0xf0eb07] /usr/bin/swift[0xef3041] /usr/bin/swift[0xe7e311] ...P1 の方が死んじゃいましたswift_decompose_double(), swift_decompose_float(), swift_decompose_float80()とswift_format_exponential()をYamsで使いたかったのだけど、libswiftCoreからexportされていないので、SwiftDtoa.hとSwiftDtoa.cppをソースコードごとコピーしてくるしか手が無い。 (edited)nm -gU /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/libswiftCore.dylibで出てくるものならリンクできる。 -g Display only global (external) symbols. -U Don't display undefined symbols.Data.withUnsafeBytes(_:)は、Dataが小さい場合にスコープ外で無効となるポインタをクロージャへ渡す場合がある。 https://bugs.swift.org/browse/SR-9578withCString(_:)は、null termination追加があるから以前から毎回一時メモリじゃないかな。 (edited) internal var isFastZeroTerminated: Bool ほほう。 https://github.com/apple/swift/blob/master/stdlib/public/core/StringObject.swift#L946struct InlineData
https://github.com/apple/swift/blob/master/stdlib/public/Darwin/Foundation/Data.swift#L681-L696protocol Animal {} protocol Cat: Animal {} protocol Owner { associatedtype Pet var pet: Pet { get } } func callBark(_ animal: Animal){} func callPetBark<T: Owner>(_ t: T) where T.Pet == Animal { callBark(t.pet) } func callPetBark<T: Owner>(_ t: T) where T.Pet == Cat { callBark(t.pet) } struct Lion: Cat {} struct Breeder: Owner { typealias Pet = Animal var pet: Animal { return Lion() } } let owner = Breeder() callPetBark(owner) assoctypeが「あるexistentialのサブタイプである」、っていう制約を書きたい (edited)BreederのpetがAnimalである時点で無理そう。 error: protocol type 'Animal' cannot conform to 'Animal' because only concrete types can conform to protocols にハマる。protocol Animal {} protocol Cat: Animal {} protocol Owner { associatedtype Pet var pet: Pet { get } } func callBark(_ animal: Animal){} func callPetBark<T: Owner>(_ t: T) where T.Pet : Animal { callBark(t.pet) } struct Lion: Cat {} struct GenericBreeder<T>: Owner { typealias Pet = T init(pet: T) { self.pet = pet } var pet: T } struct AnyAnimal : Animal { var value: Animal } struct AnyAnimalBreeder : Owner { init<X: Animal>(pet: X) { self.pet = AnyAnimal(value: pet) } var pet: AnyAnimal } struct ConcreteLionBreeder : Owner { typealias Pet = Lion var pet: Lion { return Lion() } } let o1 = GenericBreeder<Lion>(pet: Lion()) callPetBark(o1) let o2 = AnyAnimalBreeder(pet: Lion()) callPetBark(o2) let o3 = ConcreteLionBreeder() callPetBark(o3)where T.Pet : Animal と書いてしまうと、exisとは関係ないプロトコル制約の意味になっちゃって、 それが使えないねえimport Foundation enum Key: String, Decodable { case a, b, c } let json = """ { "a": "__a__", "b": "__b__", "c": "__c__" } """.data(using: .utf8)! do { print(try JSONDecoder().decode([String: String].self, from: json)) let ret = try JSONDecoder().decode([Key: String].self, from: json) print(ret) } catch { print(error) } (edited)["a": "__a__", "c": "__c__", "b": "__b__"] ⚠️ [DecodingError.typeMismatch: Value of type 'Array<Any>' required for key ''.] (edited)extension Dictionary : Decodable where Key : Decodable, Value : Decodable {
[Int: User] だったものを [UserId: User] のような感じにしたかったんですが、直接やるのは少し難しいですね let a: [Int: String] = [1: "a", 2: "b"] let b: [String: String] = Dictionary(uniqueKeysWithValues: a.map { ("\($0)", $1) })// むりやり左から右の流れに・・・ let c: [String: String] = Optional(a.map { ("\($0)", $1) }).map(Dictionary.init)!Dictionaryの代わりを作るとか…encodedOffsetをdeprecatedにする提案をswift-5.0-RELEASEへ入れるには遅すぎた、という結論へ至ったらしい。まあ仕方ない。swift-5.0-RELEASEへ入れるのを諦めるのは各offsetに対応したAPIを追加する事で、encodedOffsetをdeprecatedにして代替となるString.Index.utf16Offset(in:)とString.Index.init(utf16Offset:in:)だけはswift-5.0-RELEASEへ入れようという方向へ切り替わったのか? https://github.com/apple/swift-evolution/blob/master/proposals/0241-string-index-explicit-encoding-offset.md (edited)string.utf16.index(string.startIndex, by: offset) という書き方だと (edited)String.Index(encodedOffset: offset) でパラメータがoffsetだけだったのに対して func processEdit(editedNSRange: NSRange, changeInLenght delta: Int) { let editedRange = Range<String.Index>(uncheckedBounds: ( String.Index(encodedOffset: editedNSRange.lowerBound), String.Index(encodedOffset: editedNSRange.upperBound) )) ... public init<S: StringProtocol>(utf16Offset offset: Int, in s: S) { こうなってるな。String.Indexはオフセットが同じでも、対象となるStringにより中身が変わってきます。 コメントに構造が記されてます。 https://github.com/apple/swift/blob/master/stdlib/public/core/StringIndex.swift#L17-L33
/* String's Index has the following layout: ┌──────────┬───────────────────┬────────────────┬──────────┐ │ b63:b16 │ b15:b14 │ b13:b8 │ b7:b0 │ ├──────────┼───────────────────┼────────────────┼──────────┤ │ position │ transcoded offset │ grapheme cache │ reserved │ └──────────┴───────────────────┴────────────────┴──────────┘ - grapheme cache: A 6-bit value remembering the distance to the next grapheme boundary - position aka `encodedOffset`: An offset into the string's code units - transcoded offset: a sub-scalar offset, derived from transcoding The use and interpretation of both `reserved` and `grapheme cache` is not part of Index's ABI; it should be hidden behind non-inlinable calls. However, the position of the sequence of 14 bits allocated is part of Index's ABI, as well as the default value being `0`. */ (edited)Should the Swift language adopt an official style guide and formatting tool? これに割かれるリソースを想像すると、賛成しづらいね。 (edited)OVERVIEW: Swift Format Tool USAGE: swift [options] <inputs> OPTIONS: -help Display available options -in-place Overwrite input file with formatted file. -indent-switch-case Indent cases in switch statements. -indent-width <n> Number of characters to indent. -line-range <n:n> <start line>:<end line>. Formats a range of lines (1-based). Can only be used with one input file. -o <file> Write output to <file> -tab-width <n> Width of tab character. -use-tabs Use tabs for indentation.#available(macOS 10.13, *)がtrueになる。 @swift-5.0.3
if #available(macOS 10.13, *) { print("#available(macOS 10.13, *)") } else { print("not #available(macOS 10.13, *)") }#available(macOS 10.13, *)protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo1() -> F func foo2() -> F } struct S { func foo1() -> __opaque Foo { return 2 } func foo2() -> __opaque Foo { return 3 } } let s = S() var a = s.foo1() let b = s.foo2() a = b
/path/to/opaque-result.swift:17:5: error: cannot assign value of type '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' to type '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' a = b ^ as! (__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10) (edited)-> (some Foo, some Foo) が現時点で許されてなかった。 protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foos() -> (F, F) } struct S { func foos() -> (__opaque Foo, __opaque Foo) { return (2, 3) } } let s = S() var (a, b) = s.foos() a = b
/path/to/opaque-result-2.swift:9:21: error: 'opaque' types are only implemented for the declared type of properties and subscripts and the return type of functions func foos() -> (__opaque Foo, __opaque Foo) { return (2, 3) } ^ (edited)S を S: P にできてなかった・・・protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo1() -> F func foo2() -> F } struct S: P { func foo1() -> __opaque Foo { return 2 } func foo2() -> __opaque Foo { return 3 } } let s = S() var a = s.foo1() let b = s.foo2() a = b
/path/to/opaque-result.swift:9:8: error: type 'S' does not conform to protocol 'P' struct S: P { ^ /path/to/opaque-result.swift:5:20: note: ambiguous inference of associated type 'F': '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' vs. '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' associatedtype F: Foo ^ /path/to/opaque-result.swift:11:10: note: matching requirement 'foo2()' to this declaration inferred associated type to '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' func foo2() -> __opaque Foo { return 3 } ^ /path/to/opaque-result.swift:10:10: note: matching requirement 'foo1()' to this declaration inferred associated type to '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' func foo1() -> __opaque Foo { return 2 } ^ /path/to/opaque-result.swift:17:5: error: cannot assign value of type '(__opaque main.(file).S.foo2()@/path/to/opaque-result.swift:11:10)' to type '(__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)' a = b ^ as! (__opaque main.(file).S.foo1()@/path/to/opaque-result.swift:10:10)S の foo1 と foo2 の __opaque Foo が別の型とみなされてるっぽい。protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F } struct S: P { func foo() -> __opaque Foo { return 42 } } let s = S() var a = s.foo() let b = s.foo() a = bprotocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func makeFoo(from: Int) -> F func foo() -> F } struct S: P { func makeFoo(from: Int) -> __opaque Foo { return from } func foo() -> F { return makeFoo(from: 42) } } let s = S() var a = s.foo() let b = s.makeFoo(from: -1) print(a) a = b print(a)
42 -1foo1 と foo2 を作るとエラーになる・・・opaque-type-ast で、最近 Joe Groff が触ってるっぽい opaque-type-runtime が別にありますし。protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F func foos() -> (F, F) } extension P { func foos() -> (F, F) { return (foo(), foo()) } } struct S: P { func foo() -> Int { return Int.random(in: 0..<100) } } let s = S() var (a, b) = s.foos() print(a) a = b print(a)
9 52typealias で opaque result type はまだ許されてないようです。 protocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F } struct S: P { typealias F = __opaque Foo func foo() -> F { return 42 } } let s = S() var a = s.foo() let b = s.foo() a = b
/path/to/opaque-result-0.swift:9:19: error: 'opaque' types are only implemented for the declared type of properties and subscripts and the return type of functions typealias F = __opaque Foo ^associatedtype を確定する typealias とは別ですが、一般的な typealias は今回の Proposal 外の Future Directions に入ってるので、 protocol でも将来的なサポートになるかもです。 https://github.com/apple/swift-evolution/blob/master/proposals/0244-opaque-result-types.md#opaque-type-aliases (edited)associatedtype の型を具象型側で some Foo にした場合、 some Foo を返す複数 API 間で同一の型として扱えそうです。(some Foo)? 作れたprotocol Foo {} extension Int: Foo {} protocol P { associatedtype F: Foo func foo() -> F func fooOrNil() -> F? } struct S: P { func foo() -> __opaque Foo { return 42 } func fooOrNil() -> F? { return Bool.random() ? foo() : nil } } let s = S() var a = s.fooOrNil() print(String(describing: a)) let b: Int? = -1 a = b // Error
/path/to/opaque-result-3.swift:19:5: error: cannot assign value of type 'Int?' to type '(__opaque main.(file).S.foo()@/path/to/opaque-result-3.swift:10:10)?' a = b // Error ^ as! (__opaque main.(file).S.foo()@/path/to/opaque-result-3.swift:10:10) (edited)makeFoo() -> <F: Foo> F ↑この記法は何が起きてるかわかりやすいと思った
(edited)func foo(x: some P) { /* ... */ }
func foo<T: P>(x: T) { /* ... */ }
(edited)makeFoo() -> <F: Foo> F ←これは見た目的に直感的にそれが伝わるlet a: <F: Foo> F = makeFoo() typealias Bar = <F: Foo> Ffunc makeColl<X>() -> some Collection<.Element == X> (edited)some Foo と some Foo なら英語的に別の型を意味しそうという利点はある。struct LazyMapCollection で書けば良い気がしますが (edited)foo1 の返す some Foo には Bar と名前をつけて、 foo2 の返す some Foo には Baz と名前をつければ↓のようなことは起こらない。 var a: some Foo = foo1() let b: some Foo = foo2() a = b // `some Foo` と `some Foo` は字面上同じ型っぽいけど別の型なのでエラー-> <F: Foo> F のような記法を導入するなら何かジェネリクス的な記法で解決できないかなと。特に具体的なアイデアがあるわけじゃないけど。 typealias Bar = foo1.(some Foo) typealias Baz = foo2.(some Foo)foo1 の some Foo 間で別名を付けれてしまうので微妙。 var a: Bar = foo1()<Bar> let b: Baz = foo2()<Baz> a = b // エラー: `Bar` を `Baz` に代入できないのは自然に見える let c: Qux = foo1()<Qux> a = c // `foo1` の `some Foo` に別の名前を与えられてしまっているsome Foo ではなく -> <F: Foo> F を採用したとして、うまく馴染む記法はないかなぁと。
where 後置記法を導入しちゃったから where でやろうとすると混ざっちゃうよね。 <> の中に書けばいけるかもだけど、引数側の型パラと並ぶとややこしそう。 func makeColl<X>() -> some Collection<.Element == X> (edited)func makeColl<X: Foo>() -> <C: Collection where C.Element == X> C where X.Bar == Int (edited)where だけ後置できないの微妙。where の後置好きじゃない。: some Foo をつけずに var a = foo1() let b = foo2() a = b // エラーsome という新しいキーワードによって表現されていて (edited)func makeColl<X: Foo>() -> <C: Collection where C.Element == X> C where X.Bar == Int ↑これを推してる人もいるっぽいですね。明示的なCが手に入るのが良いところfunc makeColls() -> <C: Collection, D: Collection> (C, D) where D.Element ==C { return ([1,2,3] , [ [1], [2] ]) } こういうのが書けますねopaque-type-ast ブランチ)では変数の型として some Foo を書くことはできないみたいです。仕様なのか現状の実装状況がそうなだけなのかわからないけど。public protocol P { mutating func flip() } private struct Witness: P { mutating func flip() { /* ... */ } } public var someP: some P = Witness()public がついてるのはグローバル変数だから、ローカル変数とは扱いが異なるかも。let strings: some Collection = ["hello", "world"]some Foo で受けられるわけだし、変数で禁止する必要もなさそうに思います。
(edited)var a: some Foo (S.foo1) = s.foo1() let b: some Foo (S.foo2) = s.foo2() a = b // 納得できる代入エラー (edited)var a: some Foo (P.FooType) = s.foo1() let b: some Foo (P.FooType) = s.foo2() a = b と書かないとだめ。 (edited)some Foo はシンタックスシュガーになるみたいなので、戻り値 some Foo も後からシュガーにできるのかな? func makeFoo() -> some Foo { /*...*/ } // ↑を↓のシュガーにできる? func makeFoo() -> <F: Foo> F { /*...*/ }func useFoo(_ foo: some Foo) { /* ... */ } // ↑は↓のシュガーとして将来的に導入される模様 func useFoo<F: Foo>(_ foo: F) { /* ... */ }func makeFooPair() -> <F: Foo> (F, F) { /*...*/ }-> <F: Foo> (F, F) みたいなのを ABI 上位表現できなくなったりしないかなと。let c: LazyMapCollection = xs.lazy.map { ... }struct Wrapper<T> { var value: T } func testParsing() { let wrapped1: Int by Wrapper let wrapped2: Int by Wrapper = Wrapper(value: 5) let wrapped3 by Wrapper = Wrapper(value: 5) _ = wrapped1 _ = wrapped2 _ = wrapped3 }func testSuppressUnwrap() { var wrapped1 by WrapperWithInitialValue = 5 let z = ^wrapped1 }dyn キーワードを付けるようにするという決定が下されて、 Swift での過去の議論では同様のキーワードとして any が議論されていた。そして、そうなれば、 any Foo は existential type に、 some Foo は opaque type になると見越している?someキーワードが 、ExistentialをAnyHogehogeとして慣習的に使ってる事の対としてキレイって意見自体は、someの打診をしたスレで言ってた気がするany Foo と some Foo すごくキレイだと思うんだけど、それなら top type と bottom type も Ceylon みたいに Anything と Nothing だったらキレイだったのになぁ。Nothing の方が bottom type の本質を表している気がする。そして Nothing の対比としては Anything だろうなと。Any と Nothing だし、 Any は top type じゃなくて Any? が top とかグダグダ。Any と None とかでもいいのかもだけど、 Any と None が対として自然なのかわからない・・・。Optional の some はほぼ使うことがないからいいんじゃないかな?.some はキーワードじゃなくてただの enum の case だし。言語の一部ではない。if case let .some(foo) = foo { ... } が if case let foo? = foo { ... } みたいなシュガーが用意されてるから、 Optional.some を使うケースってほぼない気がする。opaque public typealias CertainMutableCollection<T> : MutableCollection & RangeReplaceableCollection = [T] (edited)any Foo と some Foo だとめちゃくちゃキレイ。 https://forums.swift.org/t/se-0244-opaque-result-types/21252/140 PR のブランチをチェックアウトしてコンパイラをビルドすれば… CIによってビルドされたmacOS用toolchainがPRのコメントに載っているので、それをダウンロードすれば自分でビルドしなくても試せます。 コメント: https://github.com/apple/swift/pull/21137#issuecomment-468118328 toolchain: https://ci.swift.org/job/swift-PR-toolchain-osx/216//artifact/branch-master/swift-PR-21137-216-osx.tar.gz (edited)func makeAnimals() -> <A: Animal> (A, A) { return (Cat(), Cat()) } (edited)// opaque type alias opaque typealias A: Animal = Cat func makeAnimals() -> (A, A) { return (Cat(), Cat()) }swift-5.0-RELEASE とで、SourceKitのレスポンスが違う…DocInfoTests.testDocInfoRequest()の結果がXcode 10.2と https://swift.org/download/ からダウンロードしたものの結果が違うのです。$ xcrun --toolchain org.swift.5020190325a swift test --filter testDocInfoRequest で再現できます。 (edited)sourcekittenを使ったテストでも良いですか?key.request: source.request.docinfo key.sourcetext: " /** Multiple line commented function. **/ func multiLineCommentedFunc() { } "* が普通の文字列として判定されるか、リストの bullet として判定されるかの差異ですね。 (edited)let bar: Bar = \Foo.bar(foo) (edited)func bar(_: Foo) メソッドを呼び出す keyPath になってしまう。(\Foo.bar)(foo) どうでしょう。let bar = (\Foo.bar as (Foo) -> Bar)(foo)(foo) と let bar: Bar から (Foo) -> Bar を期待していると解釈してくれれば let bar: Bar = (\Foo.bar)(foo) でも通る可能性あるかな?さすがにきついかな…。, 無い場合は普通のfunc ボディ内に書いたのと同じルールで分割されるので、ルール上の問題はないのですが、_ = [ "foo": foo .method() "bar": baz ] こういうのはつらい。<コーディング規約で禁止するしかない。 (edited)func test() { foo .method() bar } は func test() { foo.method(); bar; } であり、そのルールで動くので、上に書いた例でも問題はなくパースできるのですが、リーダビリティ的には止めて欲しいっていう意味です。 (edited)let solutions = [ 2*(1-sqrt(a))*sqrt(b+sqrt(b)) -(1+sqrt(a))(sqrt(2*b)-sqrt(2)) ] // two solutions let solutions = [ 2*(1-sqrt(a))*sqrt(b+sqrt(b)) - (1+sqrt(a))(sqrt(2*b)-sqrt(2)) ] // one solution$ docker run --privileged -it -v `pwd`:`pwd` -w `pwd` --rm norionomura/swift:pr-22714 swift Welcome to Swift version 5.0-dev (LLVM 771292f12a, Swift 474d293b05). Type :help for assistance. 1> print( 2. "hello" 3. "world" 4. ) hello worldpublic func matmul_base<T: Numeric>(lhs: [T], rhs: [T], m: Int, n: Int, p: Int) -> [T] { return matmul_internal(lhs: lhs, rhs: rhs, m: m, n: n, p: p) } @inlinable public func matmul_inlinable<T: Numeric>(lhs: [T], rhs: [T], m: Int, n: Int, p: Int) -> [T] { return matmul_internal(lhs: lhs, rhs: rhs, m: m, n: n, p: p) } public func matmul_specialize(lhs: [Double], rhs: [Double], m: Int, n: Int, p: Int) -> [Double] { return matmul_internal(lhs: lhs, rhs: rhs, m: m, n: n, p: p) } 以上のように内部実装は共通でインターフェースだけ違うのを計測してみると、 https://github.com/t-ae/inlinable-performance/blob/master/Tests/inlinable-performanceTests/inlinable_performanceTests.swift
Test Case '-[inlinable_performanceTests.inlinable_performanceTests testBase]' passed (11.864 seconds). Test Case '-[inlinable_performanceTests.inlinable_performanceTests testInlinable]' passed (11.498 seconds). Test Case '-[inlinable_performanceTests.inlinable_performanceTests testSpecialize]' passed (0.340 seconds). と素のものとinlinableで同程度になっています。 インライン化と同時にじゃないとspecializeされないってのがありそうかなーと思っています。swift test -c release で計測してる?-emit-module でモジュール作って、 -Iでインポート とかな気がする--- !Passed Pass: sil-generic-specializer Name: sil.Specialized DebugLoc: File: /Users/araki/Desktop/t-ae/inlinable-performance/Tests/inlinable-performanceTests/inlinable_performanceTests.swift Line: 30 Column: 26 Function: 'closure #1 in inlinable_performanceTests.testInlinable()' Args: - String: 'Specialized function ' - Function: '"inlinable_performance.matmul_inlinable<A>(lhs:rhs:m:n:p:)"' - String: ' with type ' - FuncType: '(@guaranteed Array<Double>, @guaranteed Array<Double>, Int, Int, Int) -> @owned Array<Double>' ... うーん?--- !Missed Pass: sil-generic-specializer Name: sil.NoDef DebugLoc: File: /Users/araki/Desktop/t-ae/inlinable-performance/Tests/inlinable-performanceTests/inlinable_performanceTests.swift Line: 31 Column: 13 Function: 'closure #1 in inlinable_performanceTests.testInlinable()' Args: - String: 'Unable to specialize generic function ' - Callee: '"XCTest.XCTAssertEqual<A>(_:_:_:file:line:)"' - String: ' since definition is not visible' ... 呼び出し側がミスしてる?// matmul_inlinable<A>(lhs:rhs:m:n:p:) sil [serialized] @$s21inlinable_performance07matmul_A03lhs3rhs1m1n1pSayxGAH_AHS3itSjRzlF : $@convention(thin) <τ_0_0 where τ_0_0 : Numeric> (@guaranteed Array<τ_0_0>, @guaranteed Array<τ_0_0>, Int, Int, Int) -> @owned Array<τ_0_0> // matmul_specialize(lhs:rhs:m:n:p:) sil @$s21inlinable_performance17matmul_specialize3lhs3rhs1m1n1pSaySdGAH_AHS3itF : $@convention(thin) (@guaranteed Array<Double>, @guaranteed Array<Double>, Int, Int, Int) -> @owned Array<Double> SIL的にはinlinableはジェネリックなままみえてそうですね-Oつけわすれてました。 つけるとmatmul_inlinableが消える代わりにmatmul_internalがジェネリックなまま出てきますね。'@inlinable' attribute can only be applied to public declarations, but 'matmul_internal' is private なるメッセージが出るのでpublic onlyと誤解した可能性が高そうです。class C { @inlinable func f() {} // error } ここでerrorって言ってるのは少なくともコンパイルエラーのことではなさそう@inlinable public func x() { let c = C() c.f() }
Class 'C' is internal and cannot be referenced from an '@inlinable' function 結局CがusableFromInlineじゃないと他のところでコンパイルが通らないですね。[omochi@omochi-iMac-PC43 mdi]$ swiftc -emit-module m.swift [omochi@omochi-iMac-PC43 mdi]$ swiftc -silgen -I . a.swift ↑これでコンパイルできたけど、実行ができない。@inline(always) とかか。$ swiftc -parse-as-library -emit-object m.swift $ swiftc -emit-module m.swift $ swiftc -I . a.swift m.o -Xlinker -rpath -Xlinker /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx $ ./main$ swiftc -emit-library m.swift $ swiftc -L . -lm -I . a.swift -Xlinker -rpath -Xlinker /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx $ ./a@inlinableにした場合、内部で使っているジェネリックメソッドも@inlinableにしておかないと、@_specializeを使わない限り完全にはspecializeされないって事だよね。これは悩ましいな。 (edited)@inlinable internal にするのがやりたいことではないです? (edited)@inlinable internalなメソッドを変更した場合、ABIが変わるって事だよね。@usableFromInline internalにすると、今度はそれを@inlinableから使った場合に遅くなってしまう。@inlinableを付けただけだと遅くなる場合がある。@_specialize(T = Int) の使いどころか?@_specialize を付けないとダメだった気がする。func gGeneric<T>(_ x: T) { f(x) } func gInt(x: Int){ f(x) } func f<T>(_ x: T) { ... }@_specializeがもっと書きやすければいいのだけど。 ジェネリックパラメータが多いと大変。 extension StringProtocol { @usableFromInline @_specialize(where Self == String, S == String, T == String) @_specialize(where Self == String, S == String, T == Substring) @_specialize(where Self == String, S == Substring, T == String) @_specialize(where Self == String, S == Substring, T == Substring) @_specialize(where Self == Substring, S == String, T == String) @_specialize(where Self == Substring, S == String, T == Substring) @_specialize(where Self == Substring, S == Substring, T == String) @_specialize(where Self == Substring, S == Substring, T == Substring) … (edited)@_specialize(where Self == String x Substring, S == String x Substring , T == String x Substring) (edited)@inlinable周りは検証し直さないといけないな。 (edited){ "": { }, "/Users/omochi/temp/mdi/m.swift": { "object": "/Users/omochi/temp/mdi/b/m.swift.o" } }
#!/usr/bin/env ruby require "shellwords" cmd = [ "swiftc", "-emit-dependencies", "-emit-module", "-emit-module-path", "b/m.swiftmodule", "-output-file-map", "map.json", "-parse-as-library", "-emit-object", "/Users/omochi/temp/mdi/m.swift" ] system(cmd.shelljoin) (edited)Result とか滑り込みだったっけ?Result よりは重要そうだけど、実装も大変そうだから余裕ももって 5.1 は回避されるかもしれないなぁ。func main() -> Int { let hoge: Int? = 1 let fuga = hoge .map { $0 * 2 } .map { $0 + 3 } .map { $0 * 4 } return fuga ?? 99 } main() (edited)error: unable to invoke subcommand: /usr/bin/swift-swiftc (No such file or directory) (edited)OVERVIEW: Swift compiler USAGE: swift OPTIONS: -assert-config <value> Specify the assert_configuration replacement. Possible values are Debug, Release, Unchecked, DisableReplacement. -continue-building-after-errors Continue building, even after errors are encountered -debug-info-format=<value> Specify the debug info format type to either 'dwarf' or 'codeview' -debug-info-store-invocation Emit the compiler invocation in the debug info. -debug-prefix-map <value> Remap source paths in debug info -D <value> Marks a conditional compilation flag as true -enable-library-evolution Build the module to allow binary-compatible library evolution -enforce-exclusivity=<enforcement> Enforce law of exclusivity -framework <value> Specifies a framework which should be linked against -Fsystem <value> Add directory to system framework search path -F <value> Add directory to framework search path -gdwarf-types Emit full DWARF type info. -gline-tables-only Emit minimal debug info for backtraces only -gnone Don't emit debug info -g Emit debug info. This is the preferred setting for debugging with LLDB. -help Display available options -index-store-path <path> Store indexing data to <path> -I <value> Add directory to the import search path -j <n> Number of commands to execute in parallel -L <value> Add directory to library link search path -l<value> Specifies a library which should be linked against -module-cache-path <value> Specifies the Clang module cache path -module-link-name <value> Library to link against when using this module -modul (edited)func main() -> Int { let hoge: Int? = 1 let fuga = hoge .map { $0 * 2 } .map { $0 + 3 } .map { $0 * 4 } return fuga ?? 99 } (edited)sil_stage canonical import Builtin import Swift import SwiftShims func main() -> Int (edited)protocol X { func say(sentence: String) func say() } struct A: X { // error: Type 'A' does not conform to protocol 'X' func say(sentence: String = "I'm fine, you ?") { print("A : \(sentence)") } } (edited)<stdin>:6:8: error: type 'A' does not conform to protocol 'X' struct A: X { ^ <stdin>:3:8: note: protocol requires function 'say()' with type '() -> ()'; do you want to add a stub? func say() ^ (edited)func foo() -> Int { return 42 } func foo(_ x: Int = -1) -> Int { return x } print(foo())foo に対して↓とか。 foo() foo(_)nil を渡してたりしたし、明示的に省略するのは良さそうな気が。public enum RGB: CaseIterable { case r, g, b } public let count = 100_000_000 @inlinable public func testB() -> Int { var result = 0 for _ in 0..<count { for c in RGB.allCases { switch c { case .r: result += 1 default: break } } } return result } このようなtestBを別モジュールから呼んだ場合RGB.allCasesで毎回Array生成しているらしくかなりコストが大きくなってしまう…… 自動実装のallCasesがinlinableじゃないってことなんでしょうけど盲点になりそう。 (edited)RGB.allCasesをforの外で呼んだ場合のtestAとの比較public enum RGB: Int, CaseIterable { case r, g, b } public let count = 100_000_000 public func testNormal() -> Int { var result = 0 for _ in 0..<count { for c in RGB.allCases { result += c.rawValue } } return result } @inlinable public func testInlinable() -> Int { var result = 0 for _ in 0..<count { for c in RGB.allCases { result += c.rawValue } } return result } inlinableなはずのtestInlinableのほうがArray生成されてtestNormalより遅くなるという結果に。RGB.allCases.getterでallocateUninitializedBuffer云々出てます。doIt { [guard weak self] in //... code }callAsFunction に決まったみたいallSatisfy 加えるなら someSatisfy 的なのもほしくないですか? and と or 的に。 https://github.com/apple/swift-evolution/blob/master/proposals/0207-containsOnly.mdcontainsOnlyのプロポーザルでしたand と or ベースで考えてたからSequence where Element == Bool に対する API がほしいかも。// x.allFalse() x.allSatisfy(!) !x.contains(true) たしかに現状使えるのだと前者のほうが短いのでこう書きたくなる(ならない)array.allSatisfy(\.self)[true, true, true].allSatisfy(\.self)<stdin>:1:31: error: cannot convert value of type 'WritableKeyPath<_, _>' to expected argument type '(Bool) throws -> Bool' [true, true, true].allSatisfy(\.self) ^~~~~~x.allSatisfy(Bool.init(_:)) いまつかえるのだとこれですね。\.selfがほぼidだよand とか or が使いやすそう。 [true, true, false].and() [true, true, false].or().allと.any (edited)allSatisfy との対では all がわかりやすいextension Sequence where Element == Bool で実装したメソッドは全ての Sequence のサジェストに出てきちゃうんですよね…w (edited)protocol LogicalOperatable 作って static func && と static func || 宣言して、extension Bool: LogicalOperatable 適合して extension Sequence where Element: LogicalOperatable で and() と or() もしくは all() と any() 作れば問題ない["a"].だとjoinedの候補でるけど、[1].だと出てこないから、extension Array where Self.Element == String はうまく動いてそうElement conforms to Sequence. joined() とか。 https://developer.apple.com/documentation/swift/arrayjoinedはArray<String>に対して書かれているのでますます謎だHTMLBuilderをXcode 11 betaで動かそうとしてるけど、いまいちうまくいかないな。 https://forums.swift.org/t/pitch-function-builders/25167buildBlockをサポートしてるのかな?buildExpressionとかも無さげ。@_functionBuilderはViewBuilderに最低限必要な機能だけ実装されてるぽい。func colorStyle<S : ColorStyle>(_ style: S.Member) -> Self.Modified<ColorStyleModifier<S>> みたいな感じでラップされて伝播してた (edited)struct RedStyle : ColorStyle ?RedStyle.Member.blue とかも生えてる?extension StaticMember where Base : ColorStyle これのせいで生えてそうMyView().colorStyle(.red) ↑これはなんで解決できるんだ? func colorStyleのSは何が推論されてるの?StaticMember.red はおかしくない?StaticMember<Base>.red の Base を何かで埋めないと。.colorStyle(.red) の推論ね。{ return .init(.init()) } の方じゃない。struct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { return .init(.init()) } static var blue: BlueStyle.Member { return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) { } } struct MyView : View {} MyView().colorStyle(.red) MyView().colorStyle(.blue)<S> StaticMember<S>.red って許されてるのかstruct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { return .init(.init()) } static var blue: BlueStyle.Member { return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) { } } struct MyView : View {} MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red) (edited)struct Static<Base> {} extension Static { static var foo: Static<Int> { return .init() } } func useMember<T>(member: Static<T>) {} useMember(member: .foo) (edited)MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red) ↑これのどっちも通るわけですよRedStyle.Member は StaticMember<RedStyle> で BlueStyle.Member は StaticMember<BlueStyle> だからBaseは不確定だけど Base = <S> S where S : ColorStyle な状態を解決としてるように思える (edited)MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red) MyView().colorStyle(.red) // これは↑のどちらでもないstruct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { print(self) return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) {} } struct MyView : View {} MyView().colorStyle(.red) MyView().colorStyle(RedStyle.Member.red) MyView().colorStyle(BlueStyle.Member.red)StaticMember<RedStyle> StaticMember<RedStyle> StaticMember<BlueStyle>.redは StaticMember<RedStyle>.red らしい。.staticProp は その返り値の型と受ける型が同一って制約があるのか?.redに対してStaticMember<S: ColorStyle> の状態で static var red をconditionalに見つけて、redの返り値が StaticMember<RedStyle> だから、.red が StaticMember<RedStyle>.red の扱いになるのかstruct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { print(self) return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) {} func anyStyle<S>(_ style: StaticMember<S>) {} } struct MyView : View {} MyView().colorStyle(.red) MyView().anyStyle(.red)StaticMember<RedStyle> StaticMember<RedStyle>struct StaticMember<Base> { init(_ base: Base) {} } protocol ColorStyle { typealias Member = StaticMember<Self> } protocol FontStyle { typealias Member = StaticMember<Self> } struct RedStyle : ColorStyle {} struct BlueStyle : ColorStyle {} struct RedFontStyle : FontStyle {} extension StaticMember where Base : ColorStyle { static var red: RedStyle.Member { print(self) return .init(.init()) } } extension StaticMember where Base : FontStyle { static var red: RedFontStyle.Member { print(self) return .init(.init()) } } protocol View {} extension View { func colorStyle<S : ColorStyle>(_ style: S.Member) {} func fontStyle<S: FontStyle>(_ style: S.Member) {} func anyStyle<S>(_ style: StaticMember<S>) {} } struct MyView : View {} MyView().colorStyle(.red) MyView().fontStyle(.red) MyView().anyStyle(.red)<stdin>:41:20: error: ambiguous use of 'red' MyView().anyStyle(.red) ^ <stdin>:16:16: note: found this candidate static var red: RedStyle.Member { ^ <stdin>:23:16: note: found this candidate static var red: RedFontStyle.Member { ^StaticMemberってStaticMember<S> で引数を受けてる意味はなくてstyle.base で <S> を取り出すだけ?func background<Background>(Background, alignment: Alignment) -> Self.Modified<_BackgroundModifier<Background>> func background<S>(S.Member) -> Self.Modified<_BackgroundModifier<Rectangle.Filled<S>>> func background<S>(S.Member, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>> func background<S>(S, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>>func background<S>(_ content: S.Member, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>> where S : ShapeStyle func background<S>(_ content: S, cornerRadius: Length) -> Self.Modified<_BackgroundModifier<RoundedRectangle.Filled<S>>> where S : ShapeStyle (edited)protocol CopyInitializable {} extension CopyInitializable { init(copy: Self) { self = copy } } class Animal : CopyInitializable { init() {} convenience init(a: Int) { let copy = Animal() // `Self`にforce castしたいがここに`Self`が書けないので // self.init(copy: copy as! Self) // type(of: self)でSelf型を作って、 // それをジェネリクスに渡すことで`as!`を実行できる self.init(copy: forceCast(copy, to: type(of: self))) } } func forceCast<X, T>(_ x: X, to type: T.Type) -> T { return x as! T }import Foundation let gregorian = Calendar(identifier: .gregorian) let utc = TimeZone(identifier: "UTC")! let components = gregorian.dateComponents(in: utc, from: Date()) func date(from components: DateComponents, with nanosecond: Int) -> Date { var components = components components.nanosecond = nanosecond return components.date! } print(date(from: components, with: 999499976).description) print(date(from: components, with: 999499977).description)2019-06-25 10:39:38 +0000 2019-06-25 10:39:39 +0000import Foundation let iso8601WithFractionalSecondFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() let gregorian = Calendar(identifier: .gregorian) let utc = TimeZone(identifier: "UTC")! let components = gregorian.dateComponents(in: utc, from: Date()) func date(from components: DateComponents, with nanosecond: Int) -> Date { var components = components components.nanosecond = nanosecond return components.date! } let date1 = date(from: components, with: 999499976) print(iso8601WithFractionalSecondFormatter.string(from: date1)) print(gregorian.dateComponents(in: utc, from: date1).nanosecond!) let date2 = date(from: components, with: 999499977) print(iso8601WithFractionalSecondFormatter.string(from: date2)) print(gregorian.dateComponents(in: utc, from: date2).nanosecond!)2019-06-25T11:26:43.999000 999499917 2019-06-25T11:26:44.000000 999500036YAMLEncoderで、10000回に5回くらいしか起きないバグの原因になってる。 (edited)DateFormatter、ドキュメントによるとdateFormat文字列には Unicode Technical Standard #35 を使うと書かれてるけど、S Fractional Secondの扱いが Unicode Technical Standard #35 と違ってるんだ。 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW13 (edited)Fractional Second - truncates (like other time fields) to the count of letters. となってるけど、実際の挙動はtruncateではなくroundになってる。 http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns (edited)public struct Date : ReferenceConvertible, Comparable, Equatable { fileprivate var _time: TimeInterval }import Foundation print(Date(timeIntervalSinceReferenceDate: 0)) print(Date().timeIntervalSinceReferenceDate) (edited)import Foundation let iso8601WithFractionalSecondFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() var d = Date(timeIntervalSinceReferenceDate: 0) for _ in 0..<10 { print(iso8601WithFractionalSecondFormatter.string(from: d)) d += 0.0001 }2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.000000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000 2001-01-01T00:00:00.001000import Foundation let iso8601WithFractionalSecondFormatter: DateFormatter = { let formatter = DateFormatter() formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSS" formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter }() let gregorian = Calendar(identifier: .gregorian) let utc = TimeZone(identifier: "UTC")! let components = gregorian.dateComponents(in: utc, from: Date()) var d = Date(timeIntervalSinceReferenceDate: 0) // 2001年 for _ in 0..<10 { print(gregorian.dateComponents(in: utc, from: d).nanosecond!) d += 0.000001 } d = Date(timeIntervalSinceReferenceDate: 86400 * 365 * 20) // 2021年 for _ in 0..<10 { print(gregorian.dateComponents(in: utc, from: d).nanosecond!) d += 0.000001 }0 1000 2000 3000 4000 5000 5999 6999 8000 9000 0 953 1907 2861 3814 4768 5722 6675 7629 8583JSONEncoderは標準機能だと、任意のDateのインスタンスを元の値を維持したままDateFormatter使ってエンコード/デコード出来ないから、Date()のround tripテストだけ特別なことしてる。 https://github.com/apple/swift/blob/master/test/stdlib/TestJSONEncoder.swift#L255-L257 (edited)DateFormatter使ったエンコード/デコードだ。Doubleにするから大丈夫だったはず。Doubleだ。JSONSerializationがDoubleの精度を落としちゃうからダメって書かれてる…JSONEncoderは任意のDateインスタンスのエンコード/デコードを標準の手法でテストされてない?ISO8601DateFormatterも精度が足りないし。DateFormatterがsub-millisecondで四捨五入する挙動はドキュメントと一致していない、というissueを登録した。 https://bugs.swift.org/browse/SR-11049Package.swiftのdependenciesを .package(url: "https://github.com/apple/swift-syntax", .revision("swift-DEVELOPMENT-SNAPSHOT-2019-07-10-m")), に書き換えてswift-5.1-DEVELOPMENT-SNAPSHOT-2019-07-09-aを使うと動いた。 (edited) .package(url: "https://github.com/apple/swift-syntax", .revision("xcode11-beta1")),Unable to format ../../SwiftLint/Package.swift: SwiftSyntax parser library isn't compatible とか言われて動かない。libSyntaxが使う型情報のハッシュを計算しておいて、実行時にこれを照合して互換性があるライブラリかチェックしてるぽい。 https://github.com/apple/swift/blob/master/utils/gyb_syntax_support/__init__.py#L164-L170 (edited)SwiftSyntaxに依存すると、TOOLCHAINにバンドルしないとほぼ成り立たないツールになってしまう様に見える。 (edited)struct Foo { var value: Int func a() -> Int { return value } mutating func b() -> Int { value += 1; return value } } print(type(of: Foo.a))(Foo) -> () -> Intstruct Foo { var value: Int func a() -> Int { return value } mutating func b() -> Int { value += 1; return value } } print(type(of: Foo.b))<stdin>:7:20: error: partial application of 'mutating' method is not allowed print(type(of: Foo.b)) ^(inout Foo) -> () -> Int じゃダメですか?is encouraged から is *required* に変わってたのか。protocol Animal: AnyObject { func foo() -> Int } class Cat: Animal { func foo() -> Int { return 42 } } let cat = Cat() let animal: Animal = cat print(MemoryLayout.size(ofValue: cat)) print(MemoryLayout.size(ofValue: animal))// A はinterface void foo(a A) { a.hoge(); a.fuga(); a.piyo(); }_swift_dynamicCastが担ってて、実装はこれ。 https://github.com/apple/swift/blob/master/stdlib/public/runtime/Casting.cpp#L2271
case MetadataKind::Existential: return _dynamicCastToExistential(dest, src, srcType, cast<ExistentialTypeMetadata>(targetType), flags);searching the methods とは書いてあるけど、そのへんは、JVM規格側なんで、__TEXT,__swift5_protoセクションにprotocol conformance descriptorへのポインタが列挙されてる。protocol conformance descriptorには以下 が含まれる。 protocol descriptorプロトコルnominal type descriptorそのプロトコルに準拠する型。メタデータとは別witness tableその型がプロトコル準拠するに必要なwitness一覧nominal type descriptorにはメタデータへのポインタが含まれる。swift_conformsToSwiftProtocolは渡されたメタデータが準拠するプロトコル情報を探してきてくれる。実装はこちら。 https://github.com/apple/swift/blob/swift-5.1-branch/stdlib/public/runtime/ProtocolConformance.cpp#L544-L608
(edited) * frame #0: 0x00000001012f3d13 libswiftCore.dylib`swift_conformsToSwiftProtocolImpl(… frame #1: 0x00000001012f3a56 libswiftCore.dylib`swift_conformsToProtocol [inlined]… frame #2: 0x00000001012f3a11 libswiftCore.dylib`swift_conformsToProtocol [inlined]… frame #3: 0x00000001012f3a11 libswiftCore.dylib`swift_conformsToProtocol… frame #4: 0x00000001012c4efa libswiftCore.dylib`swift::_conformsToProtocol… frame #5: 0x00000001012c8e07 libswiftCore.dylib`_conformsToProtocols… frame #6: 0x00000001012c849e libswiftCore.dylib`_dynamicCastToExistential(… (edited)otool -l /usr/lib/swift/libswiftCore.dylibで見る__TEXT,__swift5_protoのサイズは0x1288バイトで、含まれるprotocol conformance descriptorの数は 4744/8=593 Section sectname __swift5_proto segname __TEXT addr 0x0000000000379758 size 0x0000000000001288 offset 3643224 align 2^2 (4) reloff 0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0 (edited)YamsPackageTestsだとサイズ0x5a4でprotocol conformance descriptorの数は180__swift5_typesですね。 Section sectname __swift5_types segname __TEXT addr 0x000000000037a9e0 size 0x000000000000065c offset 3647968 align 2^2 (4) reloff 0 nreloc 0 flags 0x00000000 reserved1 0 reserved2 0Any 、引数で使われた場合は Never になるんでしょうか? protocol Animal { associatedtype Foo associatedtype Bar func foo() -> Foo func bar(_ x: Bar) -> Bar } struct Cat: Animal { func foo() -> Int { ... } func bar(_ x: Bool) -> Bool { ... } } struct Dog: Animal { func foo() -> Int { ... } func bar(_ x: String) -> String { ... } } let animal: any Animal<Foo == Int> = Bool.random() ? Cat() : Dog() print(type(of: animal.foo)) // () -> Int print(type(of: animal.bar)) // これは (Never) -> Any になる?set と get で型がずれちゃいますよね。ただ、ある引数が Never ってアクセスできないことを意味しているとも考えられて、 set が Never == readonly と解釈すればおかしくないかも。
any P で set がつぶれることと any P が P を満たすことは両立できるんじゃないかな?たとえば↓のコードで、 getSetValue に foo を渡すときに existential container を分解して特殊化されてない版を実行させることに問題はなくない? protocol Foo { associatedtype Value var value: Value { get set } } struct Bar: Foo { var value: Int } func getSetValue<F: Foo>(of foo: F) -> F { var foo: F = foo let value: F.Value = foo.value // get foo.value = value // set return foo } var foo: any Foo = Bar(value: 42) let value: Any = foo.value // foo.value = value // NG foo = getSetValue(of: foo)var a = [1, 2, 3] func f(_ index: Int) { let b = Array(a[index...]) + Array(a[..<index]) print(b) } f(0) // [1, 2, 3] f(1) // [2, 3, 1] f(2) // [3, 1, 2]fの実装に関して、簡潔に書いたらこうなったんですがパフォーマンスが悪そう どういうのがいいかなArray(a[index...] + a[..<index])にすると少しだけ良くなりそう.first(where:)をメソッドチェーンの末尾に使う場合はメソッドチェーンの開始に.lazyを入れると早くなる、とか (edited)ArraySlice みたいな型を作ってラップして返すのが良さそう。Array になってほしい時だけ選択的にコストを払って変換。Collection が SubSequence みたいに associatedtype 持っててもおもしろそう。循環コレクションから循環コレクションとっても循環コレクションのままでいいので二重にラップされないみたいな。Range の SubSequence が Range 自身なように。lazy 方式かなぁ。ShiftedArray<Element> とかより Shifted<Base: Collection> とかの方がいいかな?名前は別として。Index が Int の縛りがないと難しいかな。String の Index も Int でいいんじゃない?Shifted を生成する部分を O(1) にしたいのではなく?Shifted(str, str.index(str.startIndex, offsetBy: 4)) とか書かざるを得ないと思うのでShifted(str, 4) として 内部で同じことをすることはできるけど、まあその場合もこれ以上早い方法は無いし。Shifted<String> があったとして、その subscript のインデックスはずらして循環させた String と同じものである必要があるよね?そのインデックスを生成する手間を考えたら、そのシフトされた String を生成した方が速くない?なので、 インデックスが Int 以外のときは Shifted が不要な気がする。より正確に言えば、 Int64 とか O(1) で計算できるやつはできるけど、そこはサポートしなくてよいかと。Elementは同じものを期待してるけど、Shifted.Indexで良い String で事前に O(N) 支払うならその API が返す型は String でよくない?Collection に associatedtype ShiftedCollection: Collection where ShiftedCollection.Element == Self.Element, ShiftedCollection.Index == Self.Index を付けて func shifted(by: Int) -> ShiftedCollection として、 Array は Shifted<Array<Element>> 、 String は String を返すのがいいかなぁ。 shifted が O(1) か O(N) かが型によって変化するのが微妙かな。protocol Iterator { associatedtype Element associatedtype Failure: Error func _next() throws -> Element? } extension Iterator { func next() throws -> Element? { try _next() } } // 例外飛ばない版 // 本体の定義が`_next`なのは、もし`next`だとここで自己再起しちゃうから。。 extension Iterator where Failure == Never { func next() -> Element? { try! _next() } } // こういう仕組みが必要なのかもしれない // iterator.nextの呼び出しから例外が飛ばないことがXの型からわかるとき、 // dropCからも例外を飛ばないということを宣言したい // rethrows句自体はすでにあるけど、クロージャを渡した場合に限られた機能なので、 // これを拡張する。 func dropC<X: Iterator>(iterator: X, n: Int) rethrows(from iterator.next) { for _ in 0..<n { _ = try iterator.next() } }func next() throws(when Failure!=Never) -> Element?func next() throws(Failure) -> Element? こうかければいいんだ。func dropC<X: Iterator>(iterator: X, n: Int) throws(X.Failure) (edited)throwsは throws(Swift.Error)と同じ意味として互換性が得られるthrows 付き関数を try なしで呼んで結果を Result で受け取れてもいいように思いますが。let a = Result { try f() } で包む必要があるねResult が戻り値の関数を try 付きで呼ぶのも。try f().get() になるやつ。func f() throws(BError) -> T let r = Result<T, AError> { try f() }func f() throws(BError) -> T let r = Result { try f() } // 型パラメータ不要try!相当は欲しいですね。
() -> Void is () throws -> Void でしょ。parseInt の NumberFormatException が非検査例外なのが飛び抜けてヤバいのに印象が引っぱられてて、他はそうでもないと思うようになりました。NullPointerException で台無しってのと、 try! がないってのはその通りで、そのあたりの相乗効果で使いづらい。class MyE extends Exception{}; class A<E extends Exception> { void f() throws E {} } public class Main { public static void main(String[] args) throws Exception { try { new A<MyE>().f(); } catch (MyE e) {} } }catch(A|B|C e) { ができる。class A { typealias Error = NumberFormatException | IOException; void f() throws Error { } }A.Errorで再throwsを書ければA.f(float x) throws Error みたいな関数があったときに、 アプリモジュールで追加してる Angleみたいな型があったとして、a.f(angle)ってそのまま渡せるように A.f(Angle angle) throws Error を追加するみたいなケース。 実装としては、angle.valueを元のfに転送するだけ。enum DownloadError // ←ここに名前がつくかどうかじゃなくて { case cacheFile(FileIOError) // ←このケースと case downloadFileIOError(FileIOError) // このケースが区別できるのがtag case network(NetworkError) } (edited)DateComponents.dateがmacと比べてめちゃくちゃ遅いっぽい…… ubuntuはDocker上でしかためしてないんでオーバーヘッドはあると思いますが流石にこの差はなにかありますよね? mac: $ cat main.swift import Foundation var comps = DateComponents( calendar: Calendar(identifier: .gregorian), timeZone: TimeZone(secondsFromGMT: 0)! ) comps.year = 2019 comps.month = 11 comps.day = 10 comps.hour = 10 comps.minute = 10 comps.second = 10 comps.nanosecond = 10 let start = Date() var total: TimeInterval = 0 for _ in 0..<10_000 { total += comps.date?.timeIntervalSinceNow ?? 0 } print("time: \(Date().timeIntervalSince(start))sec, \(total)") $ swiftc -O main.swift $ ./main time: 0.011227965354919434sec, 14087090925.878277 ubuntu on dokcer on mac: root@15ebe51be255:/var/vapor-app# swiftc -O main.swift root@15ebe51be255:/var/vapor-app# ./main time: 1.0126609802246094sec, 14088576437.363556$ swiftc -O main.swift $ ./main time: 0.4973900318145752sec, 14075794190.737843$ pbpaste|docker-swift41 -O - time: 0.272868990898132sec, 14066769520.098 $ pbpaste|docker-swift42 -O - time: 0.32283997535705566sec, 14066729445.257643 $ pbpaste|docker-swift50 -O - time: 0.5901000499725342sec, 14066676853.962284 $ pbpaste|docker-swift51 -O - time: 0.5475540161132812sec, 14066619176.887285 $ pbpaste|docker-swiftnightly -O - time: 0.779636025428772sec, 14066542714.192633 $ pbpaste|swift42 -O - time: 0.013067960739135742sec, 14066457781.279984 $ pbpaste|swift50 -O - time: 0.01632702350616455sec, 14066407578.90026 $ pbpaste|swift51 -O - time: 0.014343976974487305sec, 14066365250.005945 (edited)# cat main2.swift import Foundation let df = DateFormatter() df.dateFormat = "yyyy/MM/dd HH:mm:ss.SSSSSSS" let start = Date() var total: TimeInterval = 0 for _ in 0..<10_000 { let year = 2019 let month = 11 let day = 10 let hour = 10 let minute = 10 let second = 10 let microsecond = 101010 let date = df.date(from: "\(year)/\(month)/\(day) \(hour):\(minute):\(second).\(microsecond)") total += date?.timeIntervalSinceNow ?? 0 } print("time: \(Date().timeIntervalSince(start))sec, \(total)")root@15ebe51be255:/var/vapor-app# swiftc -O main2.swift root@15ebe51be255:/var/vapor-app# ./main2 time: 0.32200801372528076sec, 14063930163.143023$ pbpaste|docker-swift42 -O - time: 0.27475202083587646sec, 14051535205.538399 $ pbpaste|swift42 -O - time: 0.012828946113586426sec, 14051471374.175034_CFCalendarComposeAbsoluteTimeV がICUを呼び出しているところだと思うんですが、 DateFormatter使った方はmacOSとそんなに変わらないのね。 targets: [ .target(name: "Complex", dependencies: ["Real"]), .target(name: "Numerics", dependencies: ["Complex", "Real"]), .target(name: "NumericsShims", dependencies: []), .target(name: "Real", dependencies: ["NumericsShims"]), ... ]sqrt の前に x * x とかでオーバーフローしないって意味ですよね?sqrt(x*x + y*y) として実装するとオーバーフローしちゃうかもしれないから (edited)float function hypot(float a, float b) { let [float x, float y] = [ // x ≥ y max(abs(a), abs(b)), min(abs(a), abs(b)) ]; if (x == 0) // x = y = 0 return 0; // avoid division by zero let float t = y / x; // 0 < t ≤ 1 return x * sqrt(1 + t * t); // > 0 }Complex に sqrt がなさそうimport Numerics let a: Complex<Double> = -1 let i: Complex<Double> = .sqrt(a) print(i) (edited)import Numerics extension Complex { static func *(lhs: Self, rhs: RealType) -> Self { return Self(lhs.real * rhs, lhs.imaginary * rhs) } static func *(lhs: RealType, rhs: Self) -> Self { return Self(lhs * rhs.real, lhs * rhs.imaginary) } } extension Complex where RealType: ElementaryFunctions { static func sqrt(_ z: Self) -> Self { let length = z.length let phase = z.phase return .sqrt(length) * Self(.cos(phase / 2), .sin(phase / 2)) } } let a: Complex<Double> = -1 let i: Complex<Double> = .sqrt(a) print(i)
(6.123233995736766e-17, 1.0)ComplexはSignedNumericじゃないんですね。 https://developer.apple.com/documentation/swift/signednumeric
func f(_ x: X) -> X { print("top-level") return x } struct X { static func f(_ x: X) -> X { print("static") return x } } let x = X() let _: X = .f(x) // staticfunc f(_ x: X) -> X { print("top-level") return x } struct X { static func f(_ x: X) -> X { print("static") return x } } let x = X() let _: X = .f(x) // staticswift-5.1-RELEASEstaticfunc f(_ x: X) -> X { print("top-level") return x } struct X { // static func f(_ x: X) -> X { // print("static") // return x // } } let x = X() let _: X = .f(x)func f(_ x: X) -> X { print("top-level") return x } struct X { // static func f(_ x: X) -> X { // print("static") // return x // } } let x = X() let _: X = .f(x)swift-5.1-RELEASE/usercode/main.swift:14:13: error: type 'X' has no member 'f' let _: X = .f(x) ~^~~~struct X {} struct Y { static func f(_ x: X) -> X { print("Y") return x } } let x = X() let _: X = .f(x)struct X {} struct Y { static func f(_ x: X) -> X { print("Y") return x } } let x = X() let _: X = .f(x)swift-5.1-RELEASE/usercode/main.swift:11:13: error: type 'X' has no member 'f' let _: X = .f(x) ~^~~~_: X = .f(x)だったらXの方で解決されますよgetCachedResultOrEmit みたいなものを経由するようになっててllvm::Value * なんだよねSILType とか出会うたびにそれが呼ばれるからasync/await だけじゃなく actor までいくかな?actor までいけば Logic Failure をハンドリングできるようになるかも。func makeAnimalPair() -> <A: Animal> (A, A) { ... }Foo<some Animal> と some Animal<.Foo == Bar> は別の話だったany も進めてほしいなぁ。 any はシンタックスだけの話だから対応は一番簡単だと思う。any 必須にするだけで?any Sequence<.Element == Int> 型引数に any Sequence<.Element == Int> を渡せないし。 (edited)some Sequence<.Element == Int> なら渡せるのおもしろいな。extension any Equatable: Equatable { static func ==... }any Sequence の Sequence 準拠するかな?AnySequence は SubSequence も決まってるしなー。AnyCollection とか Index まで決まってるし。any Sequence<Element = Int> と any Sequence<Element = Int, SubSequence = Array<Int>> はSubSequence は Collection かSequence は Iterator と Element しか持ってないんだった。typealias AnySequence<E> = any Sequence<Element = T, SubSequence = any Sequence... // ←ここが再帰すると書けないな? (edited)SubSequence 出てこない。Collection の話としてlet c: any Collection<.Element == Int> = [2, 3, 5] let sc = c[1...]sc の型はどうなるの?any Collection<.Element == Int> ? (edited)AnyCollection.SubSequence が AnyCollection だからAny になるか、コンパイルエラーのどっちか。some Collection かも。some Collection じゃないかしら?associatedtype SubSequence : Collection = Slice<Self> where Self.Element == Self.SubSequence.Element, Self.SubSequence == Self.SubSequence.SubSequenceAny まではいかなくて、↑にマッチする最も抽象的な型?Never を指定したことにして呼び出せないっていうのが良いんじゃないかlet c に束縛されたsomeです。var c だったら、再代入のたびに identityが変わる。protocol Foo { associatedtupe X var x: X { get set } }var foo: any Foo = ... let x = foo.x foo.x = ...x の型は Any で、 set のときは Never じゃないといけないよね?protocol Foo { associatedtupe X: Bar var x: X { get set } }
var foo: any Foo = ... let x = foo.x これなら x は any Bar でいける。 x の型は 空の some じゃないですかね?var foo に対しての some。get foo.x と set foo.x は、同じ型 同じだけど具体的に見えないから some。var x に再代入があったら、そこから仕切り直し。some にしようとすると x の型をコンパイル時に決定しないといけないんじゃない?var foo: any Foo = Foo1() let x = foo.x foo = Foo2() foo.x = xset は Never じゃないと。foo が some なら get して set できていいけど、 any だからインスタンスから差し替えられる。foo = Foo2() の行がある時点でfoo は別のidentityであることが静的に解析できるから。foo.x の型が変わっちゃだめじゃない?var foo: any Foo = Foo1() let x = foo.x foo = Foo2() foo.x = x ↑これは↓のような解釈 let foo_1: any Foo = Foo1() let x = foo_1.x let foo_2 = Foo2() foo_2.x = x // foo_2.Xとfoo_1.Xは違う型だからコンパイルエラーset を Never 扱いの方が自然だと思うけど。var の型が変わるというのは変だと思う。var c: AnyCollection<Int> = foo() print(c[c.startIndex])var c: AnyCollection<Int> = foo() let s = c.startIndex print(c[s]) // 絶対OK c = goo() print(c[s]) //クラッシュしうる (edited)any Collection<.Element == Foo, .Index == Int> とかでもいいわけだし。.Index == Int の指定があるなら、再代入の前後でIndexに型互換性があることをチェックできる。.Index == Hoge の指定を省略した時の挙動を考えてました。protocol Foo { associatedtype X: Bar where Baz: X var x: X { get set } }Index が contravariant position で Never になっちゃうのでいいんじゃない?ってこと。Never まで飛ばずに contravariant position を any Baz にとどめられる。Baz: X: Bar って関係ってことBaz < X < Bar って書いた方がいいかな?Never < Baz < X < Bar < Any== )だけAnyCollection はデフォルト型パラがなかったから、しかたなく型パラを制限してるだけでlet c: any Collection<E = Int> = foo() print(c[c.startIndex]) これすら使えなくなっちゃう (edited)associatedtype を型パラに持ってた方が良かったと思うんよね。get して set が必要なときは指定すればいいんだから。let c: any Collection<E = Int, I = Int> = foo() print(c[c.startIndex]) ↑こうしろって事ですか?typealias だって書けるわけだし。class C1 {} class C2: C1 {} class C3: C2 {} protocol Foo { associatedtype X: C1 where C3: X var x: X { get set } }swift: /home/buildnode/jenkins/workspace/oss-swift-5.1-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:255: typename cast_retty<X, Y *>::ret_type llvm::cast(Y *) [X = swift::DependentMemberType, Y = swift::TypeBase]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -I /Libraries/.build/x86_64-unknown-linux/debug -I /Libraries/.build/checkouts/swift-nio/Sources/CNIOAtomics/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include -I /Libraries/.build/checkouts/OpenCombine/Sources/COpenCombineHelpers/include -module-cache-path /Libraries/.build/x86_64-unknown-linux/debug/ModuleCache -D DEBUG -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOAtomics.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/COpenCombineHelpers.build/module.modulemap -module-name main -lLibraries 1. While type-checking 'Foo' (at <stdin>:5:1) 2. While validating 'Foo' (at <stdin>:5:1) /usr/bin/swift[0x493d544] /usr/bin/swift[0x493b160] /usr/bin/swift[0x493d968] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7fe4b6470390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7fe4b4995428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7fe4b499702a] /lib/x86_64-linux-gnu/libc.so.6(+0x2dbd7)[0x7fe4b498dbd7] /lib/x86_64-linux-gnu/libc.so.6(+0x2dc82)[0x7fe4b498dc82] /usr/bin/swift[0x1447f41] /usr/bin/swift[0x1447cef] /usr/bin/swift[0x1458fb0] /usr/bin/swift[0x144c03f] /usr/bin/swift[0x1470c04] /usr/bin/swift[0x14ce5b9] /usr/bin/swift[0x1455f72] /usr/protocol P{} protocol Q { associatedtype P: X }protocol P{} protocol Q { associatedtype P: X }swift-5.1.3-RELEASE/usercode/main.swift:3:21: error: use of undeclared type 'X' associatedtype P: X ^protocol P1 {} protocol P2 {} protocol Q { associatedtype X where X: P1, X: P2 }protocol P1 {} protocol P2 {} protocol Q { associatedtype X where X: P1, X: P2 }swift-5.1.3-RELEASEclass C1 {} class C2: C1 {} class C3: C2 {} protocol Foo { associatedtype X where C3: X var x: X { get set } }swift: /home/buildnode/jenkins/workspace/oss-swift-5.1-package-linux-ubuntu-16_04/llvm/include/llvm/Support/Casting.h:255: typename cast_retty<X, Y *>::ret_type llvm::cast(Y *) [X = swift::DependentMemberType, Y = swift::TypeBase]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed. Stack dump: 0. Program arguments: /usr/bin/swift -frontend -interpret - -disable-objc-interop -I /Libraries/.build/x86_64-unknown-linux/debug -I /Libraries/.build/checkouts/swift-nio/Sources/CNIOAtomics/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include -I /Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include -I /Libraries/.build/checkouts/OpenCombine/Sources/COpenCombineHelpers/include -module-cache-path /Libraries/.build/x86_64-unknown-linux/debug/ModuleCache -D DEBUG -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/CNIOAtomics.build/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/CSwiftBacktrace/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/checkouts/SwiftBacktrace/Sources/Clibunwind/include/module.modulemap -Xcc -fmodule-map-file=/Libraries/.build/x86_64-unknown-linux/debug/COpenCombineHelpers.build/module.modulemap -module-name main -lLibraries 1. While type-checking 'Foo' (at <stdin>:5:1) 2. While validating 'Foo' (at <stdin>:5:1) /usr/bin/swift[0x493d544] /usr/bin/swift[0x493b160] /usr/bin/swift[0x493d968] /lib/x86_64-linux-gnu/libpthread.so.0(+0x11390)[0x7f433d746390] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x38)[0x7f433bc6b428] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7f433bc6d02a] /lib/x86_64-linux-gnu/libc.so.6(+0x2dbd7)[0x7f433bc63bd7] /lib/x86_64-linux-gnu/libc.so.6(+0x2dc82)[0x7f433bc63c82] /usr/bin/swift[0x1447f41] /usr/bin/swift[0x1447cef] /usr/bin/swift[0x1458fb0] /usr/bin/swift[0x144c03f] /usr/bin/swift[0x1470c04] /usr/bin/swift[0x14ce5b9] /usr/bin/swift[0x1455f72] /usr/$ cat lower.swift class C1 {} class C2: C1 {} protocol Foo { associatedtype X where C2: X } $ swiftc lower.swift Stack dump: ... <unknown>:0: error: unable to execute command: Segmentation fault: 11 <unknown>:0: error: compile command failed due to signal 11 (use -v to see invocation) (edited):> 使って簡単に lower bound を書けるのかぁ。 class A {} class B extends A {} trait T[-X >: B] { def bar(x: X): Unit } class C extends T[A] { def bar(x: A): Unit = { println("C.bar: " + x) } } val c: C = new C() val t: T[B] = c t.bar(new B())C.bar: Playground$B@16418d1aclass C1 {} class C2: C1 {} protocol Foo { associatedtype X where C2: X }error: type 'C2' constrained to non-protocol, non-class type 'Self.X'class C1 {} class C2: C1 {} protocol Foo { associatedtype X where C2: X } print(1)diagnose は↑のエラーメッセージが出ることをX: AnyObject でもダメなのかな? X 自体はクラスじゃないしダメ?C: P の右側のPはプロトコルじゃないといけないから: AnyObject なら絶対クラスだよな。文言からすると protocol じゃないのが駄目なのか? (edited)X: C は書けるしな。associatedtype は仮定の話だけど、クロージャ型は現実の課題。class C1 {} class C2: C1 {} func foo<X>(_ x: X) where C2: X {}<stdin>:4:29: error: type 'C2' constrained to non-protocol, non-class type 'X' func foo<X>(_ x: X) where C2: X {} ^class BaseViewController<Inherit: UIViewController>: Inherit {}<stdin>:5:30: error: type 'C2' constrained to non-protocol, non-class type 'Self.X' associatedtype X where C2: X ^<stdin>:5:30: error: type 'C2' constrained to non-protocol, non-class type 'Self.X' associatedtype X where C2: X ^Inherit のは upper bound じゃない?それぞれ、 Inferit がBaseViewController の、 UIViewController が Inherit の。Inherit に対して BaseViewController というlower boundを与えているのでは さっきから実験している <X> C: X の形なのでInherit 不定の状態がイメージできなかったけど、ジェネリック関数の中とかだといいのか。X に対する upper/lower bounds が効いてるか。 class C1 {} class C2<X: C1>: X { func foo(_ x: X) -> X { x } func bar() { let c1: C1 = foo(self) } }template <Self> class RetainCounted<Self> { Self * retain() { ... } } class Cat: public RetainCounted<Cat> {} auto c1 = new Cat(); auto c2 = c1->retain(); // c2の型がCat*になってほしい (edited)c1 は Cat * だけど c2 は Cat ? Cat * ?とかわけわからなくなったので Swift に直してみたけど、↓みたいなことって理解で良い? class X<K> { func foo() -> K { ... } } class C: X<C> {} let c1 = C() let c2 = c1.foo() // c2の型がCになってほしいclass X<K> { var value: K { fatalError() } func foo() -> K { value } } class C: X<C> { override var value: C { self } } let c1 = C() let c2 = c1.foo() print(c2) (edited)class X<K> { var value: K { fatalError() } func foo() -> K { return value } } class C: X<C> { override var value: C { return self } } let c1 = C() let c2 = c1.foo() print(c2) (edited)print("Hello world")?: の評価は4通りやるにせよ、計算効率がめっちゃよくなる?(適切にone-wayを入れられる場所なら)numbers[indicesOfEvens] の結果のコレクションにおけるインデックスの扱いがどうなるんだろうと思ってたけど(他の言語みたいに array[range] のインデックスが 0 から始まらないのでどう整合性をとるのかなと)、↓こうなるのかぁ。 subranges, where m is the number of /// elements indicated by subranges. public subscript(subranges: RangeSet<Index>) -> DiscontiguousSlice<Self> { get set } } /// A collection wrapper that provides access to the elements of a collection, /// indexed by a set of indices. public struct DiscontiguousSlice<Base: Collection>: Collection { /// The collection that the indexed collection wraps. public var base: Base { get set } /// The set of index ranges that are available through this indexing /// collection. public var subranges: RangeSet<Base.Index> { get set } /// A position in an DiscontiguousSlice. struct Index: Comparable { // ... } public var startIndex: Index { get } public var endIndex: Index { set } public subscript(i: Index) -> Base.Element { get } public subscript(bounds: Range<Index>) -> Self { get } } ```ArraySlice とかのインデックスの取り扱いって他言語と比較して特殊だと思うんだけど( 0 スタートになるのが多い)、その利点や不利益についてどこかで論じられてたりしたっけ?Index == Int に関しては?[0] がスライスの開始点を現せるようにしたところで、 [1] ができちゃったらダメだし。 (edited)Collection プロトコルを考える時点で無理かな?Array や ArraySlice については Collection にひきづられて今の仕様になってるってこと?ゼロスタートしないこと自体に何かの利益があるわけではなくて。Array は 0 スタートだけど、 ArraySlice もコピーなく 0 スタートにできるけどしない理由についてだね。 (edited)func fifth<C: Collection>(_ c: C) -> C.Element? { return c[.first + 4] } let array = [1,2,3,4,5,6,7,8,9] print(fifth(array)!) // 5 print(fifth(array[2...])!) // 7fifth の中身では .first + 4 という形で 「4番目」と書いてるけど、array でも array[2...] でも、「4番目」が得られてる。ArraySlice を 0スタートにすると Slice にならなくなる。 ArraySliceがSliceでありながら0スタートになるようにすると、 StringのSliceが作れなくなる。ArraySlice が使いにくくなってるとしたら悲しいから、 ArraySlice が 0 始まりじゃないことによる利点はないのかな?Collection としてまとめて扱えること?ArraySlice が 0 はじまりじゃないこと自体が使いやすいケースもあると思うんだけどなぁ。if let i = absences.firstIndex(where: { $0 > 0 }) { // 1 let absencesAfterFirst = absences[(i + 1)...] // 2 if let j = absencesAfterFirst.firstIndex(where: { $0 > 0 }) { // 3 print("The first day with absences had \(absences[i]).") // 4 print("The second day with absences had \(absences[j]).") } }firstIndex 的なメソッドでは startIndex を引数に取る事が多いけどvar array = [5, 4, 3, 2, 1] array[1...3].sort() print(array) // 5, 2, 3, 4, 1 へえ。subscript set で O(N) になる?get もか。その subscript get/set の O(N) を省略できたら理想的なんだけどね。オーナーシップで扱えるようになるのかもしれないけど、そこらへんを賢くやってくれるとうれしいなぁ。sort の例ってどこから出てきたの? rintaro さんのリンク先になくない?struct GetModify { var x: String = "👋🏽 Hello" var property: String { get { print("Getting",x); return x } modify { print("Yielding",x) yield &x print("Post yield",x) } } }
https://forums.swift.org/t/modify-accessors/31872var array = [5, 4, 3, 2, 1] var slice = array[1...3] slice.sort() array[1...3] = ArraySlice(Array(slice)) print(array) (edited)subscript set しても同じ結果に。var array = [5, 4, 3, 2, 1] var slice = array[1...3] slice.sort() array[1...3] = slice print(array) // 5, 2, 3, 4, 1[5, 2, 3, 4, 1]array の 1...3 にコレクションを代入してるだけでArraySlice のインデックスが 0 はじまりでないからできることの例になってない気がする。var array = [5, 4, 3, 2, 1] var slice = array[1...3] slice.sort() array[0...1] = slice print(array)[2, 3, 4, 3, 2, 1]subscript set されるときに ArraySlice 側のインデックスが利いてくるのかと思ったんよね。ImageSlice は ArraySlice を踏襲してたつもりだけど 2 次元では真似できないな・・・。 < 幅も変えられる (edited)set がなかった・・・。サイズ違いは precondition してるのかと思ってたけど。 public subscript(xRange: Range<Int>, yRange: Range<Int>) -> ImageSlice<Pixel> { return ImageSlice(image: self, xRange: xRange, yRange: yRange) }extension スコープからしかアクセスできないようなアクセス修飾子ほしくないですか? private より狭いやつ。 @swift-5.1.5
struct Foo {} extension Foo { private var a: Int { 42 } func f() { print(a) } // OK } extension Foo { func g() { print(a + 1) } // これを禁止したい } Foo().g()f のサブの処理を a に切り出すようなときに、 a が他から可視である必要がないので。ローカル関数でもいいんですけど、スコープがネストするし個人的に読みづらい気がするんですよね。extension で共通に使われるけど他からは使われない関数とかだとローカル関数にできないし。extension をファイルを分けて private にするとかがいいんでしょうか。private は今の fileprivate だったような気もしてきました・・・。class Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())class Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())swift-5.1.3-RELEASEprivate propertyclass Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())class Foo { private var fooPrivate = "private property" } extension Foo { func bar() -> String { return fooPrivate } } print(Foo().bar())swift-3.0.1-RELEASE/usercode/main.swift:7:16: error: 'fooPrivate' is inaccessible due to 'private' protection level return fooPrivate ^ /usercode/main.swift:2:17: note: 'fooPrivate' declared here private var fooPrivate = "private property" ^private は今の fileprivate で、その後レキシカルスコープまで狭められたけど、さらにその後同一の型の extension からは参照できるように修正された?fileprivate な便利メソッドを extenion で既存の型に生やしたりします。 (edited)private な API (メソッドやプロパティ)って同じファイル内でも別の型からは呼べなくないですか? (edited)CGRect.integral() をImageResizerから呼んでいます。 import UIKit import AVFoundation public struct ImageResizer: ImageProcessing { ... } private extension CGRect { func integral(_ scale: CGFloat) -> CGRect { return CGRect(x: floor(origin.x * scale) / scale, y: floor(origin.y * scale) / scale, width: ceil(size.width * scale) / scale, height: ceil(size.height * scale) / scale) } }private extension は fileprivate になるとさっき調べていたときに見た気がします。private は fileprivate だということだったと思います。struct Foo { var value: Int } extension Foo { private var squareValue: Int { value * value } func f() { print(squareValue) } } struct Bar { var foo: Foo func g() { print(foo.squareValue + 1) } } Bar(foo: Foo(value: 42)).g()<stdin>:12:26: error: 'squareValue' is inaccessible due to 'private' protection level func g() { print(foo.squareValue + 1) } ^~~~~~~~~~~ <stdin>:6:17: note: 'squareValue' declared here private var squareValue: Int { value * value } ^squareValue の private を fileprivate にするか、 private extension Foo にしないといけないんですが、後者の場合 f のアクセスレベルにも影響があるので f を分離しないといけません。が、 fileprivate にしたいものだけ分離して private extension Foo のように書けば良いので fileprivate は要らないってことですか?private extension Foo と書くよりも、個別に fileprivate を書いた方がわかりやすいような気がします。private extension 方式で書いていると、本物の private と混同してしまったりはしませんか?しないからその方式なのかもしれませんが・・・。private func とかは結局 fileprivate なんですよね。private extension CGRect { ... } ^ こう書いて、ファイルないの他のTypeから使えるの、自然だと思ってたけど私が単にそれに慣れただけって気がしてきましたね。class Foo { ... // 同じ型なのでprivate extension Fooのメソッド・プロパティが呼べる } private extension Foo { ... } ^ これと (edited)class Foo { ... // fileprivate extension CGRectのメソッド・プロパティが呼べる } fileprivate extension CGRect { ... } これを使い分ける方が一貫性がありそう、っていう話ですね? (edited)private extension Foo { func f() { ... } } と extension Foo { fileprivate func f() { ... } } が同じになるのは、前者では字面上の private に引きづられて fileprivate ではなく private と勘違いしそうだなという感じです。僕が慣れてないだけかもしれませんが。private がほしいなと思ったと。 scopeprivate みたいな感じか、今の private を typeprivate みたいにするか。まあでも今でも複雑だし、 tarunon さんの言うようにファイル分けるが現実的そうですね。func f(_ array: [[Int]]) -> Int { array .filter { $0.count > 2 } .reduce([Int.min, Int.min]) { switch ($0, $1) { case let (max, new) where max[0] > new[0]: return max case let (max, new) where max[0] == new[0]: return max case let (max, new) where max[0] < new[0]: return new } }[1] } print(f([[0, 0], [0, 1], [0, 3]]))<stdin>:5:9: error: switch must be exhaustive switch ($0, $1) { ^ <stdin>:5:9: note: add missing case: '(_, _)' switch ($0, $1) { ^SWIFT_CROSS_MODULE_OPTIMIZATION = YESとすると、-cross-module-optimizationが有効になる。_forEachField() ってのが入ってる。 https://github.com/apple/swift/pull/29042mutable func updateAllDynamicProperties() 自体は十分に作れる素養はあるので (edited)foo( label1: { }, label2: { } ) とあんまり辛さが変わってないと思うけど・・・ (trailing closureの嬉しさは末尾の }) の回避にあると思う).first { ... } とか良い例で、初見だとナンジャコリャでしょArraySlice<Element> にできて Slice<Array<Element>> にできないことってありますか?一見、 where Base.Index == Int とか色々付けて特殊化すればパフォーマンス含めて同じにできそうに思うんですが・・・。どうして typealias ArraySlice<Element> = Slice<Array<Element>> でないのかという疑問です。初期の Swift ではできなかったから?C を持たずにバッファだけを持てば Array のがわの分だけ領域が節約できると。Array は struct だからメモリアクセスの手数は変わらなくできないかな?Slice ってサボって楽するためのものでしかなくて、個別実装した方が実行効率は良くなるのか。SliceProtocol があって、デフォルト実装いっぱいついてて、バッファの部分だけ個別実装とかもありだったのかな。Slice がある分には良さそう (edited)ArraySlice の方が効率良くて Slice はサボり用ということで納得しました。ありがとうAnyCollection だと Slice より良い実装が無い、とかあるかしら?URLSession ってまだ Alamofire が動かないくらいダメなの? https://forums.swift.org/t/alamofire-on-linux-possible-but-not-release-ready/34553callAsFunction は subscript との一貫性を考えたら subscript のように特殊構文が良かった気がする・・・。 struct Foo { invoke(a: Int, b: String) -> Bar { ... } } みたいな。 subscript が getForSubscripting や setForSubscripting でないのと同じように。 (edited)let foo = value.callAsFunction ができるのがうれしい理由の一つ?Foo.init もあるし……という気がしますね。callAsFunction って subscript と同種の存在だと思うんだけどなぁ。[] で呼び出すか () で呼び出すか。subscript はジェネリックだし・・・throws はまだできないんだっけ?struct Foo { subscript(i: Int) throws -> String { "XYZ" } } (edited)<stdin>:2:23: error: expected '->' for subscript element type subscript(i: Int) throws -> String { "XYZ" } ^ <stdin>:2:23: error: expected subscripting element type subscript(i: Int) throws -> String { "XYZ" } ^ <stdin>:2:23: error: single argument function types require parentheses subscript(i: Int) throws -> String { "XYZ" } ^~~~~~ ( ) (edited)throws はまだできないのか。昔議論されたまま放置か。// SE-0249 let getUserId1: (User) -> String = \.id // OK //print(\.id(user)) // NG print(user[keyPath: \.id]) // OK
// SE-0253 struct GetUserId { func callAsFunction(_ user: User) -> String { user.id } } let getUserId = GetUserId() //let getUserId2: (User) -> String = getUserId // NG let getUserId2: (User) -> String = getUserId.callAsFunction // OK print(getUserId(user)) // OKKeyPath は関数型に暗黙変換できるけど value(...) の呼び出しはできない。 callAsFunction は関数型に暗黙変換できないけど value(...) の呼び出しはできる。callAsFunction 、↓すらできないのか・・・。関数のように見えるけど関数として渡せないって辛くないのかな。 let ids: [String] = users.map(getUserId)//let getUserId2: (User) -> String = getUserId // NG の時点で当然な気はしますね。KeyPath と比べるとバランス悪いなと。.callAsFunction つければいいとか書いてあるけど。callAsFunction が関数型に暗黙変換できるなら、 KeyPath も単に callAsFunction 付ければ良かっただけだと思うんよね。users.map(getUserId.callAsFunction) より users.map({ getUserId($0) }) が短い() 呼び出しできないものが関数型に暗黙変換できて、 () 呼び出しできるものが関数型に暗黙変換できないって、バランスの取り方としてどうなんだと。let getUserId1: (User) -> String = \User.id // OK let getUserId2 = \.id as (User) -> String // OK let getUserId3 = \User.id as (User) -> String // NG これはバグっぽいですねcallAsFunction 、無名 func にすれば良かったのでは。そうすれば新しいキーワードも要らないし、 func が関数ということとも一貫性があるし。 struct Foo { func(_ x: Int) -> Bar { ... } } let foo = Foo() foo(42) (edited)func setForSubscripting は変って感じ? init が static func initializeAsInstance でないのは?プロパティ初期化が特殊だから?func 良かったと思うんだけどなぁ。キーワードかぶることもないし、これまでのコードを殺すこともないし。初見殺しより特定のメソッド名に役割を与える方が微妙な気が。KeyPath との変換の整合性はどう思う?KeyPath は関数型に暗黙型変換できるようになったけど、 () 呼び出しはできない。 callAsFunction が付いてる型は () 呼び出しできるけど関数型に暗黙変換できない。[keyPath: keyPath] 形式は、さっきと同じで、左辺値の概念が入ってるんで、callAsFunction が暗黙的変換されるようになったら、 SE-0249 で色々がんばらなくても KeyPath に callAsFunction 付けたらそれで済むと思うんだけど、それだと key path 式に限らず KeyPath 全般が変換できるようになるのと、 KeyPath に () でコールできるようになるのでまずいかな?foo[keyPath: \.bar] と \.bar(foo) は主従が逆だからいいんじゃないかな? callAsFunction を持つのは KeyPath 側、 subscript(keyPath:) を持つのは Foo 側。
throws! という概念を追加して、 try できるけど何も書かない場合は try! 扱い というのはどうか、という話題が発生して、 そこにクリスラトナーが、 足し算のオーバーフローや配列の範囲外アクセスを throws! であることにすれば、 エラーハンドリングできるようになる って意見を書いている https://forums.swift.org/t/handling-c-exceptions/34823/38?u=omochimetaru! が常に付いてる Optional であるのと同じノリで、 throws! な func は デフォルトで try! が付いてる(ただしはがせる) みたいな感じっすなthrows! でのインポートは、 Python interop にもあったら便利だった、というのもある。func fatalError(...) throws! -> Never とか?preconsition とか assert は最適化でエラー投げたり投げなかったりになるけどいいのか?と思ったけど、別にシグネチャ的には問題なさそう。! が氾濫するの、 Obj-C のときもそうだったけど、仕方ないとはいえ辛みがあるなぁ。 Obj-C は大分解消されてきたのに。foo.eraseToAnyPublisher() って AnyPublisher(foo) するのと何か違うんでしょうか?swift -frontend -replとかしてたまに使うintegrated REPLをswiftから削除するにあたり、使ってる人がいるかどうか調査するスレッドが出来てたので、コメントしておいた。 https://forums.swift.org/t/rfc-removing-the-integrated-repl/35441/4enum Foo { case a(Int, String) } switch Foo.a(42, "XYZ") { case .a(let x): print("\(x.0), \(x.1)") } (edited)42, XYZstderr:<stdin>:6:9: warning: cannot match several associated values at once, implicitly tupling the associated values and trying to match that instead case .a(let x): ^ (edited)print([true, true, true].allSatisfy()) print([true, false, true].allSatisfy())<stdin>:1:37: error: missing argument for parameter #1 in call print([true, true, true].allSatisfy()) ^ <#(Bool) throws -> Bool#> Swift.Sequence:3:28: note: 'allSatisfy' declared here @inlinable public func allSatisfy(_ predicate: (Self.Element) throws -> Bool) rethrows -> Bool ^ <stdin>:2:38: error: missing argument for parameter #1 in call print([true, false, true].allSatisfy()) ^ <#(Bool) throws -> Bool#> Swift.Sequence:3:28: note: 'allSatisfy' declared here @inlinable public func allSatisfy(_ predicate: (Self.Element) throws -> Bool) rethrows -> Bool ^extension Sequence where Element == Bool { func allSatisfy() -> Bool { return allSatisfy { $0 } } } print([true, true, true].allSatisfy()) print([true, false, true].allSatisfy())RangeSet https://github.com/apple/swift-evolution/blob/master/proposals/0270-rangeset-and-collection-operations.md が revert された。@extensible enumができたらいいよねみたいなスレ。if let 直したら半分くらいのコードはビルドできなくなりそう$ /usr/bin/python --version Python 2.7.16if let foo = は if case let foo? = があるけどダメ?case 省略したい気持ちはすごくわかる。 switch の case もだるい・・・。switch のcase めっちゃ書いてるな() や {} と色合わせてほしい気も? (edited)struct のプロパティで let 使った方が良さそうな新しい(?)パターンを発見した。 (参考) https://qiita.com/omochimetaru/items/7265e440418b38088ccb (edited)Identifiable みたいなやつで、 id をキーにした Dictionary を作った場合、 id か書き換えられるとキーと整合性が取れなくなる。Dictionary の使い方の問題の範疇かな?id の場合はキーとして使われやすいのでそういうケースが起こりやすいという違いはあるけど。struct Foo: Identifiable { typealias ID = Int var id: ID var value: String } let foos: [Foo] = [ .init(id: 1, value: "ABC"), .init(id: 2, value: "XYZ"), ] var idToFoo: [Foo.ID: Foo] = .init(uniqueKeysWithValues: foos.map { ($0.id, $0) }) print(idToFoo) idToFoo[1]?.id = 999 print(idToFoo)[1: main.Foo(id: 1, value: "ABC"), 2: main.Foo(id: 2, value: "XYZ")] [1: main.Foo(id: 999, value: "ABC"), 2: main.Foo(id: 2, value: "XYZ")]1 で引ける Foo の id が 999 になってしまっているのが、 id が let なら防げる。他のプロパティでも同じことだけど、 id はキーとなることが想定されているものだから(そのために Hashable が強制されてるわけだし)、 let の方が望ましそう。id と value は let 宣言しますね struct Foo: Identifiable { typealias ID = Int let id: ID let value: String }id を let にしても↓はできてしまうみたいな話ですね。 struct Foo: Identifiable { let id: Int var value: String } struct Bar { var foo: Foo } var bar = Bar(foo: Foo(id: 1, value: "ABC")) //bar.foo.id = 999 // NG bar.foo = Foo(id: 999, value: bar.foo.value) // OKvar が適切でない既知パターンとしては複数のプロパティが連動しているケースで片方だけ変更された場合に困るということがありましたが( didSet 等で辻褄を合わせるか、変更を禁止するか)、 id はキーとしての利用が想定されているので Dictionary に入れた後で変更されると困るというのは新しいパターンかなと。0 4157596543199416033 1 4157596543199416033 2 4157596543199416033 3 4157596543199416033 4 4157596543199416033 5 4157596543199416033 6 4157596543199416033 7 4157596543199416033 8 4157596543199416033 9 4157596543199416033 (edited)struct A: Hashable, CustomStringConvertible { let value: Int var description: String { value.description } func hash(into hasher: inout Hasher) { hasher.combine(0) // ハッシュは常にコンフリクトする } } let set1 = Set((0..<10).map(A.init)) let set2 = Set((0..<10).map(A.init)) set1.union(set2) .forEach { print($0, $0.hashValue) } (edited)Hashable は Equatable ですね。CVarArg めっちゃくわしいひといますか?extension Optional : CVarArg { } がなぜ動くのか知りたい..._ObjectiveCBridgeable が関係してそうなんだけど。import Foundation extension Optional: CVarArg { } struct S { } let a: Any? = S() let s = String(format: "%@", a) print(s)<stdin>:2:1: error: type 'Optional<Wrapped>' does not conform to protocol 'CVarArg' extension Optional: CVarArg { ^ Swift.CVarArg:2:9: note: protocol requires property '_cVarArgEncoding' with type '[Int]' var _cVarArgEncoding: [Int] { get } ^ <stdin>:10:15: error: incorrect argument labels in call (have 'format:_:', expected 'repeating:count:') let s = String(format: "%@", a) ^~~~~~~ repeating count: <stdin>:10:30: error: cannot convert value of type 'Any?' to expected argument type 'Int' let s = String(format: "%@", a) ^ as! IntFoundation.framework あるなしの違い... (edited)Foundation.framework に魔法があるのか// protocol witness for CVarArg._cVarArgEncoding.getter in conformance A? sil shared [transparent] [serialized] [thunk] @$sxSgs7CVarArg4mainsABP05_cVarB8EncodingSaySiGvgTW : $@convention(witness_method: CVarArg) <τ_0_0> (@in_guaranteed Optional<τ_0_0>) -> @owned Array<Int> { // %0 // user: %2 bb0(%0 : $*Optional<τ_0_0>): // function_ref CVarArg<>._cVarArgEncoding.getter %1 = function_ref @$ss7CVarArgP10Foundations21_ObjectiveCBridgeableRzrlE05_cVarB8EncodingSaySiGvg : $@convention(method) <τ_0_0 where τ_0_0 : CVarArg, τ_0_0 : _ObjectiveCBridgeable> (@in_guaranteed τ_0_0) -> @owned Array<Int> // user: %2 %2 = apply %1<τ_0_0?>(%0) : $@convention(method) <τ_0_0 where τ_0_0 : CVarArg, τ_0_0 : _ObjectiveCBridgeable> (@in_guaranteed τ_0_0) -> @owned Array<Int> // user: %3 return %2 : $Array<Int> // id: %3 } // end sil function '$sxSgs7CVarArg4mainsABP05_cVarB8EncodingSaySiGvgTW'CVarArg で struct とか Optional<T> が動かなくてひじょうにしんどいのになんかこれは動くしなんやねんos_log とかで不便すぎる"\(foo)" ってしてます。{private} とか。Any? にすればなんでもいけるようになる"{private}%s %s", "\(foo)", "\(bar)" ちょっとうろ覚えですけどこんな感じっすね。左側は必要に応じてマスクできるようにしておいて、右辺(というのか?)はStringにしちゃう。 (edited)“\(foo)”は型検査もクソもないから僕も嫌いだけど楽だから使っちゃってる (edited)Foundation.framework ない状況ないから、Any == id の世界がいい...%@ はos_log ではデフォでprivate”\(private: foo)” なら自作できそうdo { let _ = publisher.sink { value in ... } ... } ってやっちゃうと即時キャンセルされちゃうけど、 let cancellable = にしても、その後使わないから警告になっちゃうから、ただスコープの間参照を保持しつづけてくれるみたいなの。 (edited)public extension UIView { public func neko() -> String { "meow"} } が複数存在したときとかextension で someMethod が被ったとして、別ファイルで、 import Foo extension Int { func foo_someMethod() -> Int { someMethod() } } を作るなどして呼び分ける?import as とか)。まあ、最悪やらないといけなかったときになんとかする手段ということで。foo.(ModuleName)bar とかできるといいですよね。 (edited)foo.[Module]bar() (edited)protocol に適合して外部モジュールに食わせるとかもできなくなりそうです・・・。[] は subscript と衝突するかなと。[] はいいかもですね。 foo.[Module]barfoo.Module::bar よりも foo.[Module]bar の方が読みやすそう。 (edited)dropLast(while:) と suffix(while:) がほしくなった。 Array で持っていて、指定された範囲にマッチするものを抜き出したい。history .dropLast(while: { $0.date >= range.upperBound }) .suffix(while: { $0.date >= range.lowerBound }) のようなことがしたくなる。 RandomAccessCollection なら dropLast(while:) と suffix(while:) があってもいいと思う。 SortedDictionary があれば解決する話ではあるけど。suffix(while:) の方は lastIndex(where:) で意図どおりなんでしょうか?lastIndex(where:) だとインデックスしかとれないんで、それを使って SubSequence がほしいですね。firstIndex(where:) なのかと思ったのですがprefix(_:Int) と suffix(_:Int) と prefix(while:) と drop(while:) と firstIndex(where:) と lastIndex(where:) はあるんですよね。index(where:) が firstIndex(where:) になったように、 drop(while:) を dropFirst(while:) に変えたくなってしまいそうなやつですね。dropLast がないからそのままですが、 dropLast が導入されたら禍根を残しそうですね。dropLast(while:) がないのは多分複雑どがO(n)になるからじゃないですかね drop も理屈上O(n)になるのか drop(while:) はSequenceのメソッドだから、dropLast(while:) 作りたいならせめてCollectionじゃないと無理ですねRandomAccessCollection を想定しています。BidirectionalCollection でいいのかな? (edited)index(before:) が BidirectionalCollection にしかなくて、なので lastIndex(where:) も Collection にはないね。 https://developer.apple.com/documentation/swift/bidirectionalcollectionBidirectionalCollection に更新したけど、問題なくテスト通った。class NoValue {} class OneValue { var x: Int } let noValue = NoValue() let oneValue = OneValue(x: 0)<stdin>:2:7: error: class 'OneValue' has no initializers class OneValue { var x: Int } ^ <stdin>:2:22: note: stored property 'x' without initial value prevents synthesized initializers class OneValue { var x: Int } ^ = 0 <stdin>:5:16: error: 'OneValue' cannot be constructed because it has no accessible initializers let oneValue = OneValue(x: 0) ^~~~~~~~public なら、default initializerは internal になるって書いてありました。そんなこと書いてあったのか。記憶になかった。 https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID20 (edited)init() が要求されてるから、継承したクラスで init() をなくせないように required をつけなきゃいけないということですね。import Foundation import XCTest public final class EncodableTests: XCTestCase { struct S: Encodable { func encode(to encoder: Encoder) throws { var c = encoder.singleValueContainer() try c.encode(A()) try c.encode(B()) // EXC_BREAKPOINT } } struct A: Encodable { var a: Int = 1 } struct B: Encodable { var b: Int = 2 } func testEncodeDouble() throws { let encoder = JSONEncoder() _ = try encoder.encode(S()) } }func encode(to encoder: Encoder) throws { try A().encode(to: encoder) try B().encode(to: encoder) }"\(foo)" の実装がどうなるのか見た https://github.com/apple/swift/blob/87df9961a5c8ec5f6db11ae2587c13ae57ac1bd4/stdlib/public/core/StringInterpolation.swift<T> 5. Any.Type の場合 の5つのオーバーロードがあるっぽい_print_unlocked っていう謎の関数に転送されていてString.init<T>(describing: T) の実装も、 _print_unlocked への転送だった。_print_unlocked の実装は結構面白くて_openExistential を使ってる if _openExistential(type(of: value as Any), do: _isOptional) { let debugPrintable = value as! CustomDebugStringConvertible debugPrintable.debugDescription.write(to: &target) return }<T> を受けるので必ずopenできるがその先でoptionalかチェックするから、これでAnyの中に入ってるOptionalとかをおそらく必ず展開できて、 OptionalだからCustomDebugStringConvertibleが通るMirror(reflecting: value)foo はオプショナルの場合の fix-itが2つ出るが、片方は、 String(describing: foo) への書き換えだから、 String.init(describing:) の実装と 4の実装が同じ( _print_unlocked )なのは暗黙的な規約っぽい。_print_unlocked は第二引数でStreamを取れるけどstdlib internalなので、 これはユーザーに提供されていない事になる。 String.init(describing:) を使ってエミュレートできるけど、ストリーム処理可能な場合にパフォーマンスロスが出ちゃう。 public func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) { print("error \(error, "")") } ↑Optionalの時に警告出るけど、第二引数でデフォルト与えるようにしたくてOptional( ... ) いらん)extension DefaultStringInterpolation { public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) where T: TextOutputStreamable, T: CustomStringConvertible { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) where T: TextOutputStreamable { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) where T: CustomStringConvertible { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } public mutating func appendInterpolation<T>(_ x: Optional<T>, _ defaultValue: String) { if let x = x { self.appendInterpolation(x) } else { self.appendInterpolation(defaultValue) } } }UTF16View の count って内部表現が UTF-16 のときは O(1) になりますか?[Character] とか NSString とかで書いた方が良さそうですね。struct Foo { lazy var value = 1 mutating func reset() { $__lazy_storage_$_value = nil } } var foo = Foo() print(foo.value) foo.value = 2 print(foo.value) foo.reset() print(foo.value) (edited)
1$__lazy_storage_$_{name} への直接アクセスは意図されておらず、塞がれるのでご注意を。$ がキーワードとして使えるようになった影響で穴が空いた?$__lazy_... は アクセスしないために $ ついてたのかなって思った。 (edited)lazy って @Lazy にならないのかな?@Lazy var foo: Foo = ... これの時に、initialValue/wrappedValueのinitをescaping autoclosureにしていても、宣言した位置で... が評価されてるからダメみたいですね@Lazy が例に挙げられてなかったっけ?Codableの実装初期PRでは、 KeyedDecodingContainerはKeyedDecodingContainerProtocolに準拠するけど、 KeyedEncodingContainerはKeyedEncodingContainerProtocolに準拠してない、 と一貫性がなかったのを思い出した。 https://github.com/apple/swift/pull/9004#pullrequestreview-37206353 (edited)Dictionary が Array に encode される。 @swift-5.2.5
import Foundation struct Foo: Hashable { var value: String } extension Foo: Encodable { func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(value) } } let dictionary: [Foo: Int] = [Foo(value: "a"): 42] let data = try! JSONEncoder().encode(dictionary) let string = String(data: data, encoding: .utf8)! print(string)String だと object になる。JSONEncoder 側は、 encode 前に型で判断してるのか。 (edited)encode で対応しようがないよね?encode を独自実装しても?mutating func encode<T>(_ value: T, forKey key: KeyedEncodingContainer<K>.Key) throws where T : Encodable こいつが呼ばれるときに一度 JSONEncoder に処理が戻って、そこから Encodable.encode が呼ばれるか判断されるってことやんね?encode 実装で対処可能ってことは。 (edited)Dictionary のラッパーを用意して、それの encode/decode を実装することにした。さすがに保持してる型の encode/decode は色んなプロパティが生えてて辛いので。struct Entry { var manyCountArray: [Foo] mutating func update() { ... } } var map: [String: Entry] = ... for item in items { guard var entry = map[item.name] else { continue } entry.update(item) map[item.name] = entry } ↑このパターンって素朴な解釈では map[item.name] と var entry で 2つ参照があるから entry.update の中で manyCountArray の要素を変更したら配列のコピーが発生しそうだけど、 どうだろう (edited)map[item.name]?.update(item) こう書けばmodifyなので参照1つなのが表明できるんだけど。 (edited)update が manyCountArray を変更するならコピー発生するんじゃないかな?( entry に一度代入するパターンのとき) (edited)map[item.name]?.update(item) のパターンで書くとき、根っこから深くなればなるほど、そのパスをたどらないといけなくて辛いなと思うんだけど、変数に代入する代わりに inout なクロージャに渡して別名付けるとかすればいいのかな。modify(map[item.name]) { entry in entry.update(item) } 的な。map[item.name] の get から set までに map[item.name] が触られていないことを検出して、entry へのコピーを共有に書き換えてくれたらコピーされずに済みそうだけどそこまでやってくれるのかなあと思って。map[item.name]?.modify { entry in entry... entry... } って書いてます実際は
modify しようとしたら inout で同時編集のエラーにならない?foo.bar.modify { (bar) in bar.a.modify { (a) in bar.b.modify { (b) in bar.a = a + b bar.b = a - b } } } ↑これはいけるはず (edited)foo.bar.a.modify { (a) in foo.bar.b.modify { (b) in ↑これはだめ (edited) bar.a.modify { (a) in bar.b.modify { (b) in ↑ bar に対する同時編集にならない? (edited)a と b が stored property なら 分割されるsubscript は computed だから↓ができないのか。 swap(&array[i], &array[j])var foo: Foo { get } になっててダメとかある?users[i].modify { user1 in users[j].modify { user2 in guard user1.points >= points else { return } user1.points += points user2.points = min(user2.points + points, 9999) } }`
(edited)func modify<T>(_ t: inout T, _ f: (inout T) -> Void) { f(&t) } struct S { var a: Int = 3 var b: Int = 1 } var s = S() modify(&s) { (s) in modify(&s.a) { (a) in modify(&s.b) { (b) in a = a + b b = a - b } } } print(s)S(a: 4, b: 3)func modify<T>(_ t: inout T, _ f: (inout T) -> Void) { f(&t) } struct S { var a: Int = 3 var b: Int { get { _b } set { _b = newValue } } var _b: Int = 1 } var s = S() modify(&s) { (s) in modify(&s.a) { (a) in modify(&s.b) { (b) in a = a + b b = a - b } } } print(s)<stdin>:14:12: error: overlapping accesses to 's.a', but modification requires exclusive access; consider copying to a local variable modify(&s.a) { (a) in ^~~~ <stdin>:15:16: note: conflicting access is here modify(&s.b) { (b) in ^~~~Swift.swap(a[i], a[j]) は違反だけど a.swapAt(i, j) はOKfunc modify<T>(_ t: inout T, _ f: (inout T) -> Void) { f(&t) } protocol P { var a: Int { get set } var b: Int { get set } } struct S: P { var a: Int = 3 var b: Int = 1 } var s = S() func foo<T: P>(_ s: inout T) { modify(&s) { (s) in modify(&s.a) { (a) in modify(&s.b) { (b) in a = a + b b = a - b } } } print(s) } foo(&s) (edited)<stdin>:15:16: error: overlapping accesses to 's', but modification requires exclusive access; consider copying to a local variable modify(&s.a) { (a) in ^~~~ <stdin>:16:20: note: conflicting access is here modify(&s.b) { (b) in ^~~~ (edited)var s = s が必要ですねvar にコピーじゃなくて inout にした。 overlapping になった。let a = foo.bar.baz.qux a.x += 1 a.y *= 2 みたいな一時的に変数に保存して操作することができないから modify を使ってたけど、?. が挟まると writable にできない…。async なメソッドでもメソッドコール中は self が retain されてて await してる間に self が解放されることはないのかな? (edited)async/await は必ず終わる前提だから問題になることはないのか。前も似た話した気がしてきた。mutating func の中で await したらその後は変更不能になるよね?すごく使いづらそうな気が・・・。 (edited)mutating func は async mutating func の間違い?mutating func foo() async -> Barawait の後ろって非同期コールバックと同じだと思うんだけど、非同期コールバックから self を変更できないのと同じことにならない?async であることが強制されることで、書き込み先が消えないのか。async/await だと今の値型と非同期処理の相性の悪さを解消できるのか。beginAsync を使うわけで、 beginAsync を挟むと結局 self 変更できなくなる。Button("Run") { self.foo = heavyOperation() } ↑の heavyOperation が重いから非同期にしたところで、 Button("Run") { beginAsync { self.foo = await heavyOperation() } } みたいになって self を更新できない。ただし、 SwiftUI の @State なら更新できるのがややこしいけど。@State でしか更新できないのは正当なんですけど、↑のようなコードは self を書き換えられなくなるけど、 foo が値型でも @State なら書き換えられる(ので例としてちょっとややこしいけど)という意でした。 (edited)func main() { let a: Result<Int, Never> = .success(42) print(a.get()) } main()<stdin>:3:11: error: call can throw, but it is not marked with 'try' and the error is not handled print(a.get()) ^extension Result where Failure == Never { func get() -> Success { switch self { case .success(let value): return value } } } func main() { let a: Result<Int, Never> = .success(42) print(a.get()) } main()extension 標準でほしくないですか?get が throws Failure になれば解決するのかな。Foo|Never == Foo が自動的に成り立つという意味で < untaggedthrows Never は消える。Result 使ってるってのは本末転倒だと思うんですよね。async/await 入ったら Result 使いたいケースも減るし。throws に複数の型を書けるのは止めた方が良いと思います。throws に書けるのは一つの型のみcatch でも union っぽい記法になってるのおもしろい。もうどうせなら untagged union 導入しちゃえばいいのに。any Error: Error については、 throws Error が throws と等価であることを扱うためにも意味あるんじゃないでしょうか。any Error: Error が実現されてなければ同じ話になったんじゃないでしょうか? Typed Throws は不要というより単に話が進んでないだけで。Result 入れないって言ってたのに急に入れる方向に傾いた背景に、 SwiftUI と Combine があった可能性があるかもしれない。 (edited)Result を進めないといけなかったとか。一方で Typed Throws に急ぎの事情はない(し Result と違って実装も大変だ)から話が進んでない。Result は入れないという結論になったって言ってましたからね。Result だとサクッと入れられるし、 async/await や Typed Throws ですら Result と比べると相当重いアップデートですし。Result の Failure == Never のときに keypath member lookup あると便利そうじゃないですか?@dynamicMemberLookup enum MyResult<Success, Failure: Error> { case success(Success) case failure(Failure) subscript<T>(dynamicMember keyPath: KeyPath<Success, T>) -> T where Failure == Never { switch self { case .success(let value): return value[keyPath: keyPath] } } } struct User { var name: String var age: Int } let user: MyResult<User, Never> = .success(User(name: "Name", age: 42)) print(user.name)Result はエラーハンドリングにおいてサブ的な位置づけだし。 keypath memberlookup はやりすぎとしても、最初に書いた try なしの get は標準でできて良いと思います。 (edited)struct MyClass<T, Failure: Error> { @_disfavoredOverload func getResponse() -> Result<T, Failure> { fatalError() } } extension MyClass where Failure == Never { func getResponse() -> T { fatalError() } } let t = MyClass<Int, Never>().getResponse()func on(_ queue: DispatchQueue) async { await suspendAsync { continuation in queue.async { continuation(()) } } } みたいな関数を作ったら、 func foo() { let x = await bar() await on(.main) self.label.text = x } みたいなことできそう。DistatchQueue.async が async になりそう。 func foo() { let x = await bar() await DispatchQueue.main.async() self.label.text = x }await on(.main) の方が下記心地良さそう。await 相当のマークが要らないみたいだけど。flatten で回避されたやつ。 https://developer.apple.com/documentation/combine/future/replacenil(with:)func replaceNil<T>(with output: T) -> Publishers.Map<Future<Output, Failure>, T> where Self.Output == T?where Self.Output == T? の部分。 Parameterized extensions https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#parameterized-extensions 代わりの。 (edited)flatten 使ってるけど禁断なんだろうかflatten 実装できるって話になったけど、もし Parameterized Extensions が実現されたら ABI 変わっちゃうから今は追加しない方が良いのでは?みたいな話が出て取り下げられてたはず。@builder とかでもいい気も・・・。 (edited)if let と switch は使えるようになったんでしたっけ?@resultBuilder になって再Reviewになりそうな気配がする。resultBuilder でググって頑張ってって感じかな。builder は紛らわしいという指摘はあった )if 式ってなんでそんなに人気あんのかよくわかんないんですよね。巨大な式は可読性良くないし、小さな式なら三項演算子でいいし。let a = { if ... { return 1 } else { return 2 } }() ↑これよく書くけどif式なら楽だなあとは思いますreturn 1 のところで 2文書きたかったりする。let a: Int if ... { a = 1 } else { a = 2 } で書きます。 a がここで初期化されるのが保証されないというのはあるけど、普通に読めば読めると思う。: Int を書かなきゃいけないというのはあるけど、三項演算子で書けないくらい巨大になるなら型が書いてあった方が読みやすいと思うし。guard については、 if で書く場合と比べて早期脱出を意味するという意味で比較対象になりうるとは思う。if 式導入するのはわかるけど。 (edited)if 式まるごとコピペするようなときは関数にするのが正解な気も・・・。return 省略を前提に if 式はわかるけど、if 式というのは違うと思うから、「 Swift って if 式ないの?」っていうのは Swift 理解してないんだろうなという気持ち。if let ... else は無理かも? < map?? でいけるか。if で解決できないのって、プロパティの初期化とかを想定してますか?let a: Int if ... { a = 1 } else { a = 2 }[] 使ってflatMapするか
resultBuilder が利いてきそうですね。struct Foo { var bar = if let x = productionVersion { ... } else { } } = null が必須なんだけど、Optional も nil 初期化必須でいいよね。= nil って大したタイプ数じゃないし。struct Container<Value> { var value: Value init(value: Value) { self.value = value } func withValue(action: (Value) -> ()) { action(value) } } var container = Container<Any>(value: "foo") container.withValue { value in print(value) container.value = "bar" print(value) }swiftc -O a.swift && ./a と swiftc a.swift && ./a で結果が異なる (edited)init は async にできない想定なのかな?まだ実装されてないだけ? @swift-main -Xfrontend -enable-experimental-concurrency struct Foo { init() async throws { fatalError() } } (edited)<stdin>:2:12: error: initializer cannot be marked 'async' init() async throws { ^~~~~~ (edited)struct Foo { func foo() async throws { fatalError() } } (edited)-emit-sil が含まれてただけだった・・・。 (edited)init を非同期にできるのは( Promise を使う JS とかではできない)おもしろい特徴だから是非入れてほしいところ。init 入ってなかったら意見を上げたいですね。 (edited)Int https://developer.apple.com/documentation/swift/int からたどって Comparable に適合しているパスを見つけられなかったんですが、 Int ってどの経路で Comparable に適合しているかわかりますか?Numeric とかばっかり探してた・・・。stride といえば、最近 AtCoder 用にパフォーマンス計測しながらコード書いてるんだけど、 for k in 2 ... number / m { sieve[k * m] = true } っていうコードを for k in stride(from: 2, through: number / 2, by: 2) { sieve[k] = true } に書き換えたら乗算を減らせて速くなるかと思ったら激遅になっておどろいたんだけど、 Range<Int> や ClosedRange<Int> の場合だけ特殊な最適化が行われてる? (edited)+ 2 と比べてインクリメントが速い? (edited)Range の場合はイテレータとか消えて for (int i = 0; i < n; i++) とか相当になってるんだろうと予測してるんだけどstride の場合はイテレータで回してるのかなって。-Ounchecked なのは間違いない。measure した結果です。-Ounchecked で。.lazy 使って無駄な Array の生成を防ごうとしたら遅くなったとか、色々おもしろい。func primes(upTo number: Int) -> [Int] { precondition(number >= 0) if number < 2 { return [] } var sieve: [Bool] = .init(repeating: false, count: number + 1) for m in stride(from: 3, through: Int(Double(number).squareRoot() + 1.5), by: 2) { if sieve[m] { continue } let maxK = number / m if maxK < 2 { continue } for k in 2 ... maxK { sieve[k * m] = true } } var result: [Int] = [2] for m in stride(from: 3, through: number, by: 2) { if sieve[m] { continue } result.append(m) } return result } print(primes(upTo: 100))[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97][2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97][2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]import Foundation がないとコンパイルエラーになるんだけど、どういう状態だろう? Swift 5.2.1 だけど、 5.2.1 特有のバグ?そんなバグありましたか?それとも実行環境が特殊?swiftc -Ounchecked -o {dirname}/a.out {dirname}/{basename} {dirname}/a.out
https://atcoder.jp/contests/language-test-202001squareRoot って標準ライブラリですけど libc 依存なんですっけ?for m in (3...number) where !m.isMultiple(of: 2) (edited)squareRoot については内部で libc の sqrt に依存してて、環境に寄らないですよね?ということは、 import Glibc 付けとくのが良さそう。FROM ubuntu:20.04 RUN apt -y update # Install development utils RUN DEBIAN_FRONTEND=noninteractive \ apt install -y wget tar vim openssh-server sudo tmux lldb \ git cmake ninja-build \ clang python uuid-dev \ libicu-dev icu-devtools \ libedit-dev libxml2-dev \ libsqlite3-dev swig libtinfo5 \ libpython2-dev libncurses5-dev \ pkg-config libcurl4-openssl-dev \ systemtap-sdt-dev \ tzdata rsync python-six \ libncurses5 \ libz3-dev python3-distutilsimport Foundation let processInfo: ProcessInfo = .processInfo print(processInfo.operatingSystemVersionString) 結果は↓でした。 Ubuntu 5.4.0-1025.25~18.04.1-aws 5.4.60"適宜sudoをつけてください apt install clang libicu-dev libpython-all-dev wget https://swift.org/builds/swift-5.1.3-release/ubuntu1804/swift-5.1.3-RELEASE/swift-5.1.3-RELEASE-ubuntu18.04.tar.gz tar xvfz ./swift-5.1.3-RELEASE-ubuntu18.04.tar.gz mv ./swift-5.1.3-RELEASE-ubuntu18.04/usr {install_dir} export PATH={install_dir}/usr/bin:""${PATH}"" # パスを通す"
https://docs.google.com/spreadsheets/d/1PmsqufkF3wjKN6g1L0STS80yP4a6u-VdGiEv5uOHe0M/ (edited)swiftcを使うとリンクエラーになりますね。ボットはswiftを使ってる。 $ pbpaste|docker-swift-run swiftc -Ounchecked - /tmp/--90f083.o:--90f083.o:function $s4main6primes4upToSaySiGSi_tF: error: undefined reference to 'sqrt' clang-10: error: linker command failed with exit code 1 (use -v to see invocation) <unknown>:0: error: link command failed with exit code 1 (use -v to see invocation) $ pbpaste|docker-swift-run swift -Ounchecked - [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97] (edited)$s4main1aSivp ---> main.a : Swift.Intswift コマンドでimmediateモードで走らせるとLLVMのJITで実行されるからllvm.sqrtがlibmのシンボルに依存しない形で実行されるのか (edited)Int(S_IRUSR) | Int(S_IWUSR) | Int(S_IRGRP) | Int(S_IWGRP) | Int(S_IROTH) | Int(S_IWOTH) を思い出したprotocol P {} struct S: P {} extension P { static var bar: S { S() } } func foo<T: P>(_: T) {} foo(.bar): P の時に implicit member expression で参照できる (edited)protocol P { associatedtype Arg } struct S: P { typealias Arg = Int } extension P { static func bar(x: Arg) -> S { S() } } func foo<T: P>(_: T) {} foo(.bar(x: 12)) なので、たぶんこれは無理と思います。class B { static var c: C { C() } } class C: B {} func foo(_: B) {} foo(.c) ルール的にはレシーバのサブタイプなので、矛盾はしてないかな?Equatable とかのやつ、 AdditiveArithmetic とか Numeric もほしくなる。標準で付けるのは微妙だけど、コンパイラでやらないとできないし・・・。protocol P { associatedtype Foo func foo() -> Foo } extension Int: P { func foo() -> Int { self * self } } let a = (2, 3, 5) print(a.foo()) (edited)protocol P { func foo() -> Int } extension Int: P { func foo() -> Int { self * self } } let a = (2, 3, 5) print(a.foo())(Int, Int, Int).foo() が Int を返さないので。AdditiveArithmeticは横ベクトル同士の自然な加減算があるのでできなくはなさそうですよねStructure of encoded enums The following enum with associated values enum Command: Codable { case load(key: String) case store(key: String, value: Int) } would be encoded to { "load": { "key": "MyKey" } } and { "store": { "key": "MyKey", "value": 42 } }{ "kind": "store", "key": "MyKey", "value": 42 }{ "kind": "store", "value": { "key": "MyKey", "value": 42 } }enum Command { struct JSON: Codable { var load: Load? var store: Store? } }type 文で 相互に受け渡すのが楽だった。type JSON = { load: { key: string } } | { store: { key: string, value: number } } こういうことですか?type JSON = { load?: { ... } store?: { ... } } いやこっちURI 型とか TypeScript側でただの string になっちゃう。User.ID 型とかも。try と整合性が取れなくなってしまうけど、わざわざ async let で代入しなくても前者の書き方で並行になってくれたらうれしい?try との整合性」というのは https://gist.github.com/koher/ca74ef2c882418f683d06092f4408c3c のような意味です。 (edited)async let で 1 回代入挟むくらいがバランス良さそうですね。await hoge.foo().bar() って呼べるので嬉しい、これがコンフリクトしそうだなとawait hoge.foo(bar()) みたいなの (edited)async let は一見わかりにくいけど、文法として新しいコードの形になっている(これまでの文法として、letの左にasyncは書けない)ので、暴発することはない。 (edited)await でまとめた式は並列になる」は、async/awaitで新しく導入されるといっても、「await がついてない部分について実行順序の挙動が変わる」のは、 これまでと変化がある部分に対して見た目の変化がないのでわかりづらいと思う。fileprivate 書かなきゃいけないケースってあまりないなと思ってたんですが、↓のようなケースで fileprivate でしか書けなさそうなのを発見しました。 struct S {} private struct T {} private protocol P { func foo() -> T } extension S: P { fileprivate func foo() -> T { T() } }let s = readLine()! let a = (0..<2).map(s.dropFirst).count を実行して 11111111111111111 など 15 or 16 文字以上の文字列を標準入力として与えると -O または -Ounchecked 時に実行時エラーになるそうです。気持ち悪い・・・。 https://twitter.com/_kebo/status/1357011619440599044let s = readLine()! let a = (0..<2).map(s.dropFirst).count を実行して 11111111111111111 など 15 or 16 文字以上の文字列を標準入力として与えると -O または -Ounchecked 時に実行時エラーになるそうです。気持ち悪い・・・。 https://twitter.com/_kebo/status/1357011619440599044 s.dropFirst を変数に束縛するのでも再現しなくなるみたいですし、{ s.dropFirst($0) } のようにクロージャにしても再現しなくなりますし、あとは (ちゃんと記録してませんでしたが) 形を変えて行った時にどこかで二重解放の実行時エラーが出ていたので、s が解放されちゃいけないタイミングで解放されているのかなぁという気がしてます。 かといって withExtendedLifetime で s のライフタイムを伸ばしても変わらなかったので、何処かの最適化フェーズに問題があったのではと睨んでます。concurrentPerform の引数を1にするとわかりやすいんですが、最後に0のデータが来るかどうかがランダムなんですよね。// おまじない func print(_ item: String) { fputs(item + "\n", stderr) }availableData が空だったらEOFだからhandlerをnilにするってのを何件かみたんですが、 ドキュメントだと使い方が不明瞭ですね……低レイヤなれてる人なら常識なのかもしれませんが。 https://developer.apple.com/documentation/foundation/filehandle/1412413-readabilityhandler
https://developer.apple.com/documentation/foundation/filehandle/1411463-availabledata
availableData が空ならEOFというのは合ってそうですが、readabilityHandlerのほうはEOFが来ることを保証していないような気がする。readToEnd はmacOS10.15.4からなのにPackage.swiftでは.v10_15 しか指定できない……var s = 1 var a: Int = s var b: Int? = s のaとbが異なるということですね。クラスサブクラス、あるいはExistentialと具体型の関係なら、これは同じものを指すわけです。(Existentialは厳密には異なるが省略) var s = Cat() var a: Animal = s var b: Cat = s この結果発生する問題として、 実態が異なっているということを知っているユーザーと知らないユーザーとの間で、期待する挙動に差が出てきます。例えば.some(1) と 1 は別でしょ」っていう理解のユーザーからは、AnyHashableの値が異なることこそが期待される挙動なはずです。var s = Cat() var a: Animal = s // CatとAnimalのサブタイプ関係が成り立つことを示す var b: Cat? = s var c: Animal? = b U?はT?のサブタイプ、は具体のコードとしては、 UITableViewController? な変数が UIViewController? に渡せる、というのが頻出すると思います。 (edited)var s = Cat() var a: Animal = s // CatとAnimalのサブタイプ関係が成り立つことを示す var b: Cat? = .some(s) var c: Animal? = b.map { $0 as Animal } mapのところは本当は違うと思うんですが、まあ雑にこんな感じ。 (edited)var d: Cat?? = b // b: Cat?1. オプショナルのサブタイプなので.someでラップする 2. CatはCat?のサブタイプなのだから、.mapでOK// 1. var d1 = .some(b) // 見たまま.some(.none)になります // 2. var d2 = b.map { $0 as Cat? } // これは.noneになります じゃあこれがどういう問題を引き起こしたかというとWrappedとして扱えるように隠蔽していた部分でエッジケースで齟齬が出るのはしょうがないという感じもしますねvar s = Cat() var a: Animal = s // CatとAnimalのサブタイプ関係が成り立つことを示す var b: Cat? = s var c: Animal? = b U?はT?のサブタイプ、は具体のコードとしては、 UITableViewController? な変数が UIViewController? に渡せる、というのが頻出すると思います。 (edited)UIView? の間違い?protocol IntCollection: RangeReplaceableCollection where Self.Element == Int {} こういうふうにassociatedtypeを埋めて再定義してやれば使えるけどany Collection<Element = Int> みたいにパラメータを埋められる、OrderedSet/Dictionary もいいけど SortedSet/Dictionary も欲しくないですか?ツリーで実装されてて、各種操作が O(log N) で、イテレートしたらソート済で全要素 O(N) で取得できるやつ。let a: (foo: Int) a = (foo: 42) print(a)<stdin>:1:9: error: cannot create a single-element tuple with an element label let a: (foo: Int) ^~~~~ <stdin>:2:5: error: cannot assign value of type '(foo: Int)' to type 'Int' a = (foo: 42) ^~~~~~~~~(T) と T が同じものですよねlet a: (Int) = 42let a: (Int) = 42 print(type(of: a)) // Intlet f: () -> (Int) = { 0 } print(type(of: f)) // () -> IntT と (T) は同じものですべての値は single element なタプルだと思ってる。内部的な扱いは別として、概念的に。func foo() -> (isUpdated: Bool) { ... }enumerated (オフセットしか返してくれない)ではなく。見たところ見当たらない。 @swift-5.3.3
let a: ArraySlice<Int> = [0, 1, 2, 3].dropFirst(2) for (i, x) in a.enumerated() { print(i, x) } for i in a.indices { print(i) } 0 2, 1 3 ではなく 2 2, 3 3 を得たい。zip(x.indices, x)
ArraySlice 使ってるときとか使いづらくて・・・。でも zip(x.indices, x) で良さそうですね。.indexed() のほうがzipに同じx が2回書かれている事を読み取らなくていいので、filterでかけるときはreduceを使わなくて良いのと同じで、 indexedのほうが良いと思います 名前も自明だし。indexed() がいいのはもちろんとして、わざわざそのために Swift Algorithms を入れたくないようなケースでは、ですね。-> の前に async が要りそう。 extension BankAccount { func meetsTransactionLimit(_ limit: Amount) -> Bool { return try! await self.lastTransaction.amount < limit // ^~~~~~~~~~~~~~~~ // this access is async & throws } }unsafelyUnwrapped の利用例こんなところで見つけた-O でさえチェックコストが消えるらしい。
extension Sequence { func sorted<T>(by keyPath: KeyPath<Element, T>) -> [Element] where T: Comparable { sorted(by: { $0[keyPath: keyPath] < $1[keyPath: keyPath] }) } }
// ↓みたいにして使う users.sorted(by: \.age)keyPaths: KeyPath<Element, T>... を受け取るようにしようとしたけど、 Swift の Comparable は同値判定がないから Equatable もないとだめだな。a == b は !(a < b) && !(b<a) ですよComparable: Equatable だった。これは知らなかった。extension Sequence { func sorted<T>(by keyPaths: KeyPath<Element, T>...) -> [Element] where T: Comparable { sorted(by: { for keyPath in keyPaths { let l = $0[keyPath: keyPath] let r = $1[keyPath: keyPath] if l < r { return true } else if l > r { return false } } return false }) } }reversed あるからなくてもいい気がする。array.sort(Comparators.key(\.a).key(\.b, .desc))T... と KeyPath<Element, T>... の ... が対応することを示さないといけなくて構文上きついなぁ。 extension Sequence { func sorted<T...>(by keyPaths: KeyPath<Element, T>...) -> [Element] where T: Comparable { // 省略 } }private func _cmp<Value: Comparable>(_ lhs: Value, _ rhs: Value) -> Bool? { lhs == rhs ? nil : lhs < rhs } private func _cmp<Root, Value: Comparable>( _ lhs: Root, _ rhs: Root, on keyPath: KeyPath<Root, Value> ) -> Bool? { _cmp(lhs[keyPath: keyPath], rhs[keyPath: keyPath]) } こういうの用意しておいて array.sorted { _cmp($0, $1, on: \.prop1) ?? _cmp($0, $1, on: \.prop2.other) ?? false } でいいや、って最近なりました。func sorted<T>(by keyPath: KeyPath<Element, T>) と、複数のキーの場合は都度クロージャ式を実装でもいい気も・・・。 3 キーくらいまで extension 用意しておけば 98% くらいカバーできそう。struct Foo<Bar> { struct Baz {} } があるときに、 Foo<Bar>.Baz であってほしいときと Foo.Baz であってほしいときないですか?単に Foo に関連する型をネームスペースとして Foo. にまとめたいときに Foo<Bar>.Baz になると鬱陶しいことが。Foos を作って解消するのが良さそうFoo.Bazを書けるようにする、みたいな議論あるのかな (edited)Foo.Bazを書けるようにする、みたいな議論あるのかな (edited)enum でネームスペースにしてるのとか微妙だし。struct Foo<Bar> { } struct Foo.Baz { } こういうふうに書けたらいいんちゃうかと思ったことある。extension はそうですしね。Foo<Bar>.Baz と Foo.Baz の共存とかあり得る?もっと言えば nested type と namespace で名前が被るとどうなる??struct Foo<Bar> { } struct Foo.Baz { } こういうふうに書けたらいいんちゃうかと思ったことある。 async let 単体のピッチが立った。ちゃんとした提案書もついてる。 提案書の中で、 TaskGroup.spawn が TaskGroup.async に変更されていて、 spawn let ではなく async let なのはそれに揃ってるという事になっている。await しなければ静的エラーだろうって話を以前したけど、 この文書だと、スコープ脱出時にキャンセルしてキャンセル完了を待機するって仕様になっている。 1つ目のタスクの結果を見て2つ目のタスクの結果を捨てるみたいなフローを考慮したっぽい。 (edited)spawn の方がいいんじゃないかって意見を書き込んだら、 async let になった経緯として、 複数のAPIを async に統一するって方針の議論がある事を教えてもらった。let foo: Foo if let f = self.foo { foo = f } else { foo = Foo(...) self.foo = foo }let foo: Foo = self.foo.value(default: Foo(...))Dictionary の dictionary[key, default: value] += 1 みたいなノリで。let foo = self.foo.ensure { Foo(...) }
@discardableResult public mutating func ensure(_ f: () throws -> Wrapped) rethrows -> Wrapped { if let x = self { return x } let x = try f() self = x return x }@autoclosure でいい気がする。 (edited)nil のときだけ初期化だからやっぱクロージャがいいか。ensure 普通に標準ライブラリにほしいね。toggle, subscript[_:default:], reduce(into:_) とかの仲間としていけるんじゃないかなぁ。 public mutating func consume<R>(_ f: (Wrapped) throws -> R) rethrows -> R? { guard let x = self else { return nil } self = nil return try f(x) }self.foo.consume { $0.shutdown() }if let foo = self.foo { foo.shutdown() self.foo = nil }Bool の toggle が通ったことを考えるとこれもいけるのでは? Dictionary の subscript(_:default:) は同じじゃないですか?foo って4回書くのが1回で済むので結構使ってます。struct で不必要に let つかうのやめようぜっていったら猛攻をくらった...let を使いたくなる気持ちはわからんでもないですけどね。原理的に変えられることと、変えるための手軽な手段が存在することは異なるので。ただ、境界があいまいになるので、突き詰めると↑になっちゃうんですよね・・・。let にすれば十分なケースまでプロパティ側を全部 let にするのは許容しづらいですが。let で宣言してれば問題ないと思いますね。
[User] がレスポンスで返ってくるようなケースを考えると、 Array は mutaing func を持つので、それは問題にならないの?って話になりますしね。それが許容できるなら(利用側で let users: [User] で問題ないなら) struct のプロパティも var でいいでしょという。struct Point { let x: Int let y: Int init(x: Int, y: Int) { self.x = x; self.y = y } } extension Point { mutating func setX(_ x: Int) { self = .init(x: x, y: self.y) } } という理解でいいですか?struct Point { let x: Int let y: Int init(x: Int, y: Int) { self.x = x; self.y = y } } extension Point { mutating func setX(_ x: Int) { self = .init(x: x, y: self.y) } } という理解でいいですか? private(set) var で同じ事が起きますよ (edited)Bool の toggle() で、 var 使ってる以上プロパティをどれだけ守っても可変だし、可変であっても共有されないから本質的にイミュータブルと同じなんだよなぁ。let にして守った気になってるけど self 書き換えを extension で生やせるから守れてなくて、一部どころか全体すら書き換えられるから、イミュータブルに見える Bool ですらイミュータブルでないし、それで問題ないって意味。toggle って参照型中心の世界の考え方に浸かってるとぶったまげると思うんよね。Bool や String がミュータブルってだけで泣いちゃいそう。extension Bool { var negate: Bool { !self } } の方があり得る。けどこれは!を使えで蹴散らされるtoggled() は無いのか。struct A { let i: Int } struct B { let a: A? = nil } //let _: [Any] = [B()].compactMap { $0[keyPath: \B.a?.i] } // OK let _: [Any] = [B()].compactMap(\.a?.i)import Foundation struct Foo: Codable { var value: Int?? } let foo: Foo = .init(value: .some(.none)) print(foo) let data = try! JSONEncoder().encode(foo) print(String(data: data, encoding: .utf8)!) let decoded = try! JSONDecoder().decode(Foo.self, from: data) print(decoded)Foo(value: Optional(nil)) {"value":null} Foo(value: nil)import Foundation struct Foo: Codable { var value: Int?? } let foo: Foo = .init(value: .some(.none)) print(foo) let data = try! JSONEncoder().encode(foo) print(String(data: data, encoding: .utf8)!) let decoded = try! JSONDecoder().decode(Foo.self, from: data) print(decoded) enum の Codable の話の文脈で話されてるっぽいけど、そもそも単体でこれまでに起こってた話なんですよね。String? とかが Codable でないのはそれはそれで辛いですね。うーん。"some": { "_0": { "some": { "_0": {...} } } } } とかで表現すればいいわけだ。"some": { "_0": { "some": { "_0": {...} } } } } とかで表現すればいいわけだ。 Optional って今どうなってるんだ?と思ってやってみたら↑みたいになってたという・・・。でも、 98% くらいの用途である Foo? が自動 Codable 形式になるのは辛いですよね・・・。 (edited)T? だけ特別扱いは出来なくはなさそうですよね。T? の T が Optional でないときみたいな条件書けますっけ? Optional.init(from:)/Optional.encode(to:) のstdlib実装なら可能だと思います。という意図でした。 (edited)Wrapped が Optional なら分岐させるっていう意味です。もちろん evolution proposal 必要ですけど。actor Counter { private(set) var count: Int = 0 func countUp(_ amount: Int) -> Int { count += amound return count } func countUp() -> Int { countUp(1) } } (edited)final class Counter { private let queue: DiqpatchQueue = ... // Serial Queue private var _count: Int = 0 var count: Int { get async { withCheckedContinuation { continuation in queue.async { continuation.resume(returning: _count) } } } } private func _countUp(_ amount: Int) -> Int { count += amound return count } func countUp(_ amount: Int) async -> Int { withCheckedContinuation { continuation in queue.async { continuation.returning(returning: _countUp(amount)) } } } private func _countUp() -> Int { _countUp(1) } func countUp() async -> Int { withCheckedContinuation { continuation in queue.async { continuation.resume(returning: _countUp()) } } } } (edited)f (_ a: @escaping () async -> Void) { let s = DispatchSemaphore(value: 0) async { await a() s.signal() } s.wait() }a が元のスレッドに突入しようとしたらダメじゃないでしょうか?f をメインスレッドで呼び出して a に DispatchQueue.main.async の async/await 版を渡すとか。f (_ a: @escaping () async -> Void) { let s = DispatchSemaphore(value: 0) async { await a() s.signal() } s.wait() } asnyc の中身が f を実行してる スレッドに投入される可能性がありますね。 async/awaitではブロッキングが起きないことを前提にスケジューラが構成されているらしいので、 他のTaskの処理がそのwaitしてるスレッドに投入されて複雑に噛み合った結果止まる可能性もあります。LIBDISPATCH_COOPERATIVE_POOL_STRICT で実行時に死ぬか確かめるしかないのか... わかるけど...@MainActor 以外に thread 特定されるような要素ってなにがあるかなasnyc ではなく DispatchQueue.asnyc で書いているはずで、Task を作ったところで Task オブジェクトを引き回しておかないとcurl みたいな CLI を URLSession の async でつくるってできないのかも。async {} aka Task.init {} か何かが必要 (edited)semaphore.signal()/semaphore.wait() のTask.init で起動するけど wait できないって状態か@MainActor 以外、cooperative thread pool の thread 以外は使われないから、さっきの f() が Task から呼ばれないことが保証されていれば絶対安全なんじゃないかなあ。Task.unsafeCurrentTask をチェックするassertionをいれとこう
notTask(_ t: @escaping () -> Void) みたいな (edited)@main struct MyProgram { static func main() async { ... } }
https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#launching-async-tasks@main struct MyProgram { static func main() async { ... } }
https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md#launching-async-tasks --parse-as-library つけないと @main つかえない… (別の話ですが) (edited)@main 必須かなあ @MainActor @Sendable func _doMain(_ asyncFun: @escaping () async throws -> ()) async { do { try await asyncFun() } catch { _errorInMain(error) } } Task.detached { await _doMain(asyncFun) exit(0) } _asyncMainDrainQueue() これが実装で、Task.detached して exit(0), main は CFRunLoopRun() ということらしい。 (edited)actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { async { print(inc()) } } } @main struct Main { static func main() async { let a = A() await a.f() print(await a.c) } }actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { async { print(inc()) } } } _runAsyncMain { let a = A() await a.f() print(await a.c) }1 1stderr:<stdin>:10:9: warning: 'async(priority:operation:)' is deprecated: `async` was replaced by `Task.init` and will be removed shortly. async { ^func f() async ですか?それともbodyの async { .. } ですか?async { .. } を外すと(今の所)必ず実行されてるように見える (edited)func f() async ですか?それともbodyの async { .. } ですか? func f() async にするとデッドロックしなくなった。これでniwさんの環境を再現できた気がするawait a.f() 以下と fの async { .. } ブロックの中が同じスレッドで実行されるようになるっぽい…import Foundation actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() async { async { print("begin f") print(Thread.current) print(inc()) print("end f") } } } _runAsyncMain { print(Thread.current) let a = A() await a.f() print("begin main") print(Thread.current) print(await a.c) print("end main") }<Thread: 0x00007fd110000c60> begin main <Thread: 0x00007fd114002dd0> 0 end main begin f <Thread: 0x00007fd114002dd0> 1 end fstderr:<stdin>:13:9: warning: 'async(priority:operation:)' is deprecated: `async` was replaced by `Task.init` and will be removed shortly. async { ^import Foundation actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { async { print("begin f") print(Thread.current) print(inc()) print("end f") } } } _runAsyncMain { print(Thread.current) let a = A() await a.f() print("begin main") print(Thread.current) print(await a.c) print("end main") }<Thread: 0x00007f3114000c60> begin f <Thread: 0x00007f3118003360> 1 end f begin main <Thread: 0x00007f3114000c60> 1 end mainstderr:<stdin>:13:9: warning: 'async(priority:operation:)' is deprecated: `async` was replaced by `Task.init` and will be removed shortly. async { ^func msg(f: String = #function, _ s: Any) { print("\(Thread.current): \(f): \(s)") } actor A { var c = 0 func inc() -> Int { c += 1 return c } func f() { msg("3") async { msg("4") print(inc()) } } } @main struct Main { static func main() async { msg("1") let a = A() await a.f() msg("2") print(await a.c) } } (edited)<NSThread: 0x7fa109c0bfd0>{number = 1, name = main}: main(): 1 <NSThread: 0x7fa109d05540>{number = 2, name = (null)}: f(): 3 <NSThread: 0x7fa109d05540>{number = 2, name = (null)}: f(): 4 1 <NSThread: 0x7fa109c0bfd0>{number = 1, name = main}: main(): 2 ^Cmake: *** [run] Interrupt: 2 deadlockした場合<NSThread: 0x7f9250d0bfb0>{number = 1, name = main}: main(): 1 <NSThread: 0x7f9250e05b80>{number = 2, name = (null)}: f(): 3 <NSThread: 0x7f9250e05b80>{number = 2, name = (null)}: f(): 4 1 <NSThread: 0x7f9250d0bfb0>{number = 1, name = main}: main(): 2 1 deadlockしなかった場合actor A { func g() { } func f() { async { g() } } } @main struct Main { static func main() async { let a = A() await a.f() await a.f() // ここでdeadlock.. したりしなかった } } (edited)async(priority: .background) { などpriority変えても起きますか?AsyncSequence に Publisher の代わりをさせるとして、 combineLatest や merge 相当のことを簡潔に実現するのは難しいでしょうか? AsyncSequence に combineLatest や merge が生えれば解決?AsyncStream が出てきたら割と何でも出来るようになると思います。combineLatest 作るとしたら↓みたいに並行でループ回してとかでしょうか? var values: (S1.Element?, S2.Element?) = (nil, nil) { didSet { switch values { case (let e1?, let e2?): // (e1, e2) をイテレータに流す case (.nil, nil): // nil をイテレータに流す default: break } } } async { for await e in s1 { values = (e, values.1) } } async { for await e in s2 { values = (values.0, e) } }AsyncStream { (cont) in var a: A! var b: B! async { for await x in a_s { a = x cont.yield(a, b) } } async { for await x in b_s { b = x cont.yield(a, b) } } } (edited)AsyncSequence に combineLatest はほしいな。combineLatest とかはサードパーティ提供になりそうな気がするなぁ。Sequence と同じのしかないですよねー。guard let でシャドーイングすると、 { } の中では(バインド先の変数にアクセスできないのは当たり前として)元の変数にもアクセスできなくなる。コードリーディング時の勘違いを減らすにはこれでいいのかもしれないけど・・・。 guard let response = response as? HTTPURLResponse else { assertionFailure("Never reaches here.") print(type(of: response)) return } (edited)print(type(of: response)) の response がシャドーイング前のものって読み取りづらい気もするから、まあ妥当な制約かもなぁと。これまで遭遇したことなかったからこんなケースあったのかと思って。↑のケースでは結局 guard let httpResponse = response as? ... にした。do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 }do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 } do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 } do { defer { print(a) } // ←まだaが宣言されてないよ…? let a = 0 } ... お、swiftbot-sandbox でさんざん実験済みだったのか笑 (edited)<stdin>:2:19: error: use of local variable 'a' before its declaration defer { print(a) } // ←まだaが宣言されてないよ…? <stdin>:3:9: note: 'a' declared here let a = 0 ^(some P)? みたいなのができるようになるっぽい?// ERROR: function declares an opaque return type, but has no return statements in its body from which to infer an underlying type // func asHOFRetArg() -> (some P) -> () { return { (x: String) -> () in } } // これなんでinferできないんだろう (edited)@_optimize([none|size|speed]) これおもしろい。 関数ごとに最適化モード変えられるんだ。@_optimize(none) func fib1() {...} @_optimize(speed) func fib2() {...} こういうのを用意したら確かめられるかな?@_private(sourceFile: "FileName.swift")
URLSession の新しい data(from:) メソッドを Task の cancel メソッドでキャンセルしたら、 CancellationError じゃなくて code == NSURLErrorCancelled な NSError が throw されるVersion 13.0 beta 5 (13A5212g)URLError にas でキャストできますよe.code == .cancelled かなdata(from:) メソッドの実装で code をチェックして CancellationError を投げるべきでは?Task がキャンセルされているかをチェックして URLSessionDataTask をキャンセルしてるコードがあるはずで、そこで管理すべき?checkCancellation が投げるデフォルトのエラーとして CancellationError は定義されてるけど、Concurrencyのキャンセル機構としてそこは実装依存かCancellationError に寄せるのは難しいと思います (edited)CancellationError が var source: Error? を持つようにすれば解決できそうな?CancellationError は isCancelled == true のときに Task.checkCancellation が投げるエラーというだけで、キャンセルされたときに throw されるエラーは CancellationError とは限らないという仕様なのかな? catch let error as CancellationError でキャンセルをチェックするのではなく、 catch 節の中で Task.isCancelled でチェックするのが正しい? (edited)URLSession の data(from:) がどこで実装されてるのか見つからない・・・。CancellationError()
CancellationError を throw しなくても良い?でも CancellationError
CancellationError を throw することが期待されている? URLSession.data(from:) が cancel 時に CancellationError を throw しないのはお行儀が悪い? https://github.com/apple/swift-evolution/blob/main/proposals/0304-structured-concurrency.md#cancellation
(edited)CancellationError, to communicate that the task was cancelled. CancellationError として検出できるように、くらいの意味まで持つならキャンセル時に独自のエラーを throw するのではなく CancellationError を throw すべきとなりそうだけど。
do { try await foo() } catch let error as CancellationError { // キャンセル時の処理 } catch { // エラーハンドリング } とするよりも do { try await foo() } catch { if Task.isCancelled { // キャンセル時の処理 } else { // エラーハンドリング } } がベストプラクティス? (edited)cancel メソッドを呼んだレシーバのTaskに対してキャンセルフラグが立つ実装になっています。cancel メソッドを呼んだレシーバのTaskに対してキャンセルフラグが立つ実装になっています。 Task.checkCancellation() を呼ぶか、 Task.isCancelled をチェックして適切な処理を行った後に CancellationError を throw するというルールなのかと思っていました。
catch 節で Task.isCancelled でチェックするのが良さそうですね。 (edited)func download(...) async throws -> Data を呼び出して、自分がキャンセルボタンを押した場合と何かしらエラーが発生した場合のどちらもエラーが throw されるけど、前者では何もしないけど後者ではアラートを出したいみたいなケースを想定しています。 (edited)try { await download() } catch { if Task.isCancelled { throw error // or return } switch error { case let error as HogeError: // ... } } みたいな感じになるんですかね? (edited)try { await download() } catch { if Task.isCancelled { throw error // or return } switch error { case let error as HogeError: // ... } } みたいな感じになるんですかね? (edited)Task.isCancelled だとTask インスタンスとしてどこかに保持されていると思うので、 そのTaskに対して task.isCancelled するのが良いと思います。Task インスタンスとしてどこかに保持されていると思うので、 そのTaskに対して task.isCancelled するのが良いと思います。 catch でエラーが飛んできたところで分岐しないといけなくないですか?それとも Task { } の中で catch せずに task.result で結果を受け取った方が良い?var downloadTask: Task<Data, Error>? func didTapDownload() { let task = Task { try await download(...) } self.downloadTask = task do { let result = try await task.value } catch { if task.isCancelled { ... } } } func didTapCancell() { self.downloadTask?.cancel() } こういうのを想像してました。 (edited)var downloadTask: Task<Data, Error>? func didTapDownload() { let task = Task { try await download(...) } self.downloadTask = task do { let result = try await task.value } catch { if task.isCancelled { ... } } } func didTapCancell() { self.downloadTask?.cancel() } こういうのを想像してました。 (edited)didTapXX はasyncにできないからStructuredにしちゃいけないですねdownload の戻り値をわざわざ Task の外側でハンドリングするのも若干気持ち悪い気もするし、 task.value を async の中に入れるためにもう一つ Task 作るとかはもっと気持ち悪いので仕方なさそうですね。 (edited)func download(url: URL) async throws -> Data? { var urlSessionTask: URLSessionTask? return try withTaskCancellationHandler { return try await withUnsafeThrowingContinuation { continuation in urlSessionTask = URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // Ideally translate NSURLErrorCancelled to CancellationError here continuation.resume(throwing: error) } else { continuation.resume(returning: data) } } urlSessionTask?.resume() } } onCancel: { urlSessionTask?.cancel() // runs immediately when cancelled } } withTaskCancellationHandler使って手動でチェックするのがいいんですかね?(結局呼び出し側でError Typeチェックしなければいけないので、あまり意味ない気もしますが...)task.cancel が呼び出されたときに、 download 関数の実装者は URLSessionTask を即時 cancel したいけど、それを実現するために使えるという。CancellationError が throw されるようにしたいという意識なのかな。ただ、 100% 保証は無理だから、やっぱり呼び出し側では as CancellationError で判別するのではなく Task.isCancelled が良さそう。
onCancel に Sendable でない Box を渡せてしまってるのはなぜ??? func download(url: URL) async throws -> Data? { let urlSessionTask: Box<URLSessionTask> = .init() return try await withTaskCancellationHandler { return try await withUnsafeThrowingContinuation { continuation in let task = URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { // Ideally translate NSURLErrorCancelled to CancellationError here continuation.resume(throwing: error) } else { continuation.resume(returning: data) } } task.resume() urlSessionTask.value = task } } onCancel: { urlSessionTask.value?.cancel() // runs immediately when cancelled } } final class Box<Value> { var value: Value? } onCancel: { [urlSessionTask] in urlSessionTask?.cancel() // runs immediately when cancelled } Sendableチェックはmainでも起きませんか?おそらくこのPRがrelease/5.5に入ってないからチェックされてないんじゃないかなーと思いました。 https://github.com/apple/swift/pull/38866 SwiftFiddleでやってみたらmainの場合warningが出ました。 /main.swift:21:7: warning: cannot use let 'urlSessionTask' with a non-sendable type 'Box<URLSessionTask>' from concurrently-executed code urlSessionTask.value?.cancel() // runs immediately when cancelled ^ /main.swift:25:13: note: generic class 'Box' does not conform to the `Sendable` protocol final class Box<Value> { (edited)nil の状態で capture されるだけで変更が反映されなくないでしょうか?
Sendable のチェックはまだお預けなんですね・・・。
Sendable の status も↓ですしね。 @MainActor も付けてないのに↓のコンパイルエラーになるのなぜ??そして Task.detached の代わりに Task.init を使うと起こらない・・・。 Version 13.0 beta 5 (13A5212g)
final class UserViewController: UIViewController { let userID: User.ID var user: User? ... override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID) self.user = user // ⛔ Property 'user' isolated to global actor 'MainActor' can not be mutated from a non-isolated context } catch { // エラーハンドリング } } } }UIViewController に付いてのか。 @MainActor class UIViewController : UIResponder
https://developer.apple.com/documentation/uikit/uiviewcontrollerTask.init だと MainActor 上で実行されるけど、 detached だとそうじゃないからエラーになってしまったと。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID) DispatchQueue.main.async { [self] in self.user = user } } catch { // エラーハンドリング } } }MainActor に戻したらいけたけど、 DispatchQueue.main よりもいい方法ないんだっけ?DispatchQueue.main って型じゃないけど、どうやって MainActor と紐付いてるの?( DispatchQueue.global() だとコンパイルエラー)extension UserViewController { func setUser(_ user: User?) { self.user = user } } 作っておいて await self.setUser(user) とか? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { @MainActor [self] in do { let user = try await fetchUser(for: userID) self.user = user } catch { // エラーハンドリング } } }extension UserViewController { func setUser(_ user: User?) { self.user = user } } 作っておいて await self.setUser(user) とか? setUser も actor-isolated でダメじゃないですか?await するのか。UserViewController のメソッドは暗黙で MainActor isolatedになるので、他の Actor コンテキストからは暗黙に async なので await で呼べると。await self.user = user とかできるんだけど、そうなってないし、そうなっていない理由があるんじゃないかと。async は getter only ですもんね、今のところ。Task.detached(priority: .background) { @MainActor [self] in って Task.init と実質かわらなくないです?Task.detached(priority: .background) { @MainActor [self] in って Task.init と実質かわらなくないです? MainActor だけど Task の priority は .background っていうのはあり得るのかと思いましたが、 priority は queue ごとに決まっていてそんなことはない? DispatchQueue の priority って queue を作るときに決めますもんね。setUser を作るのもなぁ・・・。 DispatchQueue.main.async { self.user = user } か、そうでなければ↓? Task { @MainActor in self.user = user }@MainActor in はいらないと思います。await { @MainActor in self.user = user }()
@MainActor in はいらないと思います。 @MainActor in はいらない Task.init ではなく Task.detached を挟んで context を引き継がないときなので MainActor に戻す必要があるかと。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID); Task { @MainActor in self.user = user } } catch { // エラーハンドリング } } }
@MainActor in はいらない Task.init ではなく Task.detached を挟んで context を引き継がないときなので MainActor に戻す必要があるかと。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID); Task { @MainActor in self.user = user } } catch { // エラーハンドリング } } }
await { @MainActor in self.user = user }()
await 付けないといけないことに気付いてなかったんですけど、そしたらコンパイラがクラッシュしました override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { [self] in do { let user = try await fetchUser(for: userID); await { @MainActor in self.user = user }() } catch { // エラーハンドリング } } }await { @MainActor in self.user = user }()
await 付けないといけないことに気付いてなかったんですけど、そしたらコンパイラがクラッシュしましたfunc foo() async { { @MainActor in }() } でクラッシュ再現しました。報告しておきますTask.detached で作られた Task 上で実行されて、 Task { @MainActor in だと新しい Task 上で実行されるのが違いか。ただし、どちらでも main スレッド( DispatchQueue.main 上)で実行されると。DispatchQueue はキューが priority を持ってるけど、 Swift Concurrency では actor (キューの保持者)ではなく Task が priority を持っている。 Task が suspend されたときに resume される優先度が Task の priority で決まると思ってたけど、 actor は suspend した Task の priority に関わらずキューに突っ込んだ順に resume する?それとも MainActor は DispatchQueue.main を使っている関係で Task の priority を無視して前から処理する? (edited)Since actors are designed for reentrancy, the runtime may choose to move the higher-priority item to the front of the queue, ahead of the lower-priority items. This way, higher-priority work could be executed first, with lower-priority work following later. This directly addresses the problem of priority inversion, allowing for more effective scheduling and resource utilization.
https://developer.apple.com/videos/play/wwdc2021/10254/?time=2160
actor は入れ替えてくれそうな感じがしますね。 DispatchQueue も DispatchWorkItem に qos を指定できるみたいですね。使ったことなかった。 https://developer.apple.com/documentation/dispatch/dispatchworkitem
MainActor でも priority を考慮してくれそう。 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task.detached(priority: .background) { @MainActor in do { let user = try await fetchUser(for: userID) self.user = user } catch { // エラーハンドリング } } }
@MainActor のクロージャ式が丸ごと↓の block に突っ込まれるイメージ? DispatchQueue.main.async(DispatchWorkItem(qos: .background, block: /* ここ */)) (edited)DispatchQueue.mainはSerial Queueなので、qosが高いDispatchWorkItemが追加されても、前のDispatchWorkItemのqos上げて先に実行してしまわないんでしょうか?(priority inversionが起きる?)DispatchQueue.mainはSerial Queueなので、qosが高いDispatchWorkItemが追加されても、前のDispatchWorkItemのqos上げて先に実行してしまわないんでしょうか?(priority inversionが起きる?) @expanded を追加する提案 これがついている引数は、その型の init の引数を、直接その関数の引数として渡せる。 (edited)protocol P { associatedtype Value } protocol Q: P where Value == Int {} このQ を作るのをサボりたいってことですか?protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() }protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() } protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() }protocol Base { func foo() func bar() } protocol Derived: Base { func foo() override func bar() } <stdin>:7:10: warning: implicit override should be marked with 'override' or suppressed with '@_nonoverride' func foo() ^ override <stdin>:2:10: note: overridden declaration is here func foo() ^-warn-implicit-overrides flag when building the standard library and overlays, so that each protocol member that overrides a member of an inherited protocol will produce a warning unless ...public protocol BaseP { func foo() func bar() } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public struct Concrete: DerivedP { public func foo() {} public func bar() {} } のときの witness table が sil_witness_table Concrete: DerivedP module test { base_protocol BaseP: Concrete: BaseP module test method #DerivedP.bar: <Self where Self : DerivedP> (Self) -> () -> () : @$s4test8ConcreteVAA8DerivedPA2aDP3baryyFTW // protocol witness for DerivedP.bar() in conformance Concrete } sil_witness_table Concrete: BaseP module test { method #BaseP.foo: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3fooyyFTW // protocol witness for BaseP.foo() in conformance Concrete method #BaseP.bar: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3baryyFTW // protocol witness for BaseP.bar() in conformance Concrete } で @_nonoverride と明示された requirement は別の witness になる。BidirectionalCollection.index(_:offsetBy:) is the most obvious example, because the BidirectionalCollection ’s version of index(_:offsetBy:) allows negative indices. RandomAccessCollection ’s version is also marked @_nonoverride because it is required to be asymptotically faster than the Collection or BidirectionalCollection versions.@_nonoverride に関しては public protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public extension DerivedP { func foo() { print("DerivedP.foo()") } func bar() { print("DerivedP.bar()") } } public struct Concrete: DerivedP {}sil_witness_table Concrete: DerivedP module test { base_protocol BaseP: Concrete: BaseP module test method #DerivedP.bar: <Self where Self : DerivedP> (Self) -> () -> () : @$s4test8ConcreteVAA8DerivedPA2aDP3baryyFTW // protocol witness for DerivedP.bar() in conformance Concrete } sil_witness_table Concrete: BaseP module test { method #BaseP.foo: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3fooyyFTW // protocol witness for BaseP.foo() in conformance Concrete method #BaseP.bar: <Self where Self : BaseP> (Self) -> () -> () : @$s4test8ConcreteVAA5BasePA2aDP3baryyFTW // protocol witness for BaseP.bar() in conformance Concrete } こうなるんだけどprotocol witness for BaseP.bar() を呼び出す方法がわからないので、 public protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public extension DerivedP { func foo() { print("DerivedP.foo()") } func bar() { print("DerivedP.bar()") } } public struct Concrete: DerivedP { } func testBase<T: BaseP>(x: T) { x.foo() x.bar() } func testDerived<T: DerivedP>(x: T) { x.foo() x.bar() } testBase(x: Concrete()) testDerived(x: Concrete())public protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public extension DerivedP { func foo() { print("DerivedP.foo()") } func bar() { print("DerivedP.bar()") } } public struct Concrete: DerivedP { } func testBase<T: BaseP>(x: T) { x.foo() x.bar() } func testDerived<T: DerivedP>(x: T) { x.foo() x.bar() } testBase(x: Concrete()) testDerived(x: Concrete()) DerivedP.foo() DerivedP.bar() DerivedP.foo() DerivedP.bar()protocol Base { func foo() } protocol Derived: Base { @_nonoverride func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("Impl.foo1") } @_implements(Derived, foo()) func foo2() { print("Impl.foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl())protocol Base { func foo() } protocol Derived: Base { @_nonoverride func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("Impl.foo1") } @_implements(Derived, foo()) func foo2() { print("Impl.foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl()) Impl.foo1 Impl.foo2BidirectionalCollection.index(_:offsetBy:) は、RandomAccessCollection.index(_:offetBy:) は、一気にオフセットしたインデックスを作ってアクセスできるpublic protocol BaseP { func foo() func bar() } public extension BaseP { func foo() { print("BaseP.foo()") } func bar() { print("BaseP.bar()") } } public protocol DerivedP: BaseP { override func foo() @_nonoverride func bar() } public struct Concrete: DerivedP {} でエラーにならないのでそういうわけでもなさそう@_nonoverride 相当だったのを override 相当にすることによって witness table が小さくなってサイズ削減できたって話っぽいですよね。override も nonoverride も付けない場合で、サブプロトコルで同じ名前をrequirementsに書くことができて、その場合テーブルエントリが別にできるわけですね。This makes restating protocol requirements in inheriting protocols ABI-neutral, so we can restate protocol requirements to influence associated type inference without baking those decisions into the ABI.protocol Base { func foo() } protocol Derived: Base { func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("foo1") } @_implements(Derived, foo()) func foo2() { print("foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl())protocol Base { func foo() } protocol Derived: Base { func foo() } struct Impl: Derived { @_implements(Base, foo()) func foo1() { print("foo1") } @_implements(Derived, foo()) func foo2() { print("foo2") } } func callBase<T: Base>(_ x: T) { x.foo() } func callDerived<T: Derived>(_ x: T) { x.foo() } callBase(Impl()) callDerived(Impl()) foo1 foo1Derived.foo に @_nonoverride をつけると、2行目の出力が foo2 に変わる。AsyncSequenceでOptionalを扱いたい場合、nextの戻り値の型をOptionalのOptionalにしなければいけないのは、ループの終わり記号としてのnilと、要素としてのnilを区別するために仕方がないんですかね?(AsyncStream との比較のためにあえてAsyncSequence 使ってます)IteratorProtocol と同じですね(失礼しましたmm)。 mutating func next() async throws -> Self.Element?
struct IntGenerator: AsyncSequence { typealias Element = Int? var strings: [String] struct AsyncIterator: AsyncIteratorProtocol { var current = 1 var strings: [String] fileprivate var index = 0 mutating func next() async -> Int?? { // <- ここ guard index < strings.count else { return nil } let string = strings[index] index += 1 return Int(string) } } func makeAsyncIterator() -> AsyncIterator { AsyncIterator(strings: strings) } } Task { let strings: [String] = ["1", "a", "2"] for await i in IntGenerator(strings: strings) { print(i) } }
Optional(1) nil Optional(2) (edited)AsyncSequenceでOptionalを扱いたい場合、nextの戻り値の型をOptionalのOptionalにしなければいけないのは、ループの終わり記号としてのnilと、要素としてのnilを区別するために仕方がないんですかね?(AsyncStream との比較のためにあえてAsyncSequence 使ってます)IteratorProtocol と同じですね(失礼しましたmm)。 mutating func next() async throws -> Self.Element?
struct IntGenerator: AsyncSequence { typealias Element = Int? var strings: [String] struct AsyncIterator: AsyncIteratorProtocol { var current = 1 var strings: [String] fileprivate var index = 0 mutating func next() async -> Int?? { // <- ここ guard index < strings.count else { return nil } let string = strings[index] index += 1 return Int(string) } } func makeAsyncIterator() -> AsyncIterator { AsyncIterator(strings: strings) } } Task { let strings: [String] = ["1", "a", "2"] for await i in IntGenerator(strings: strings) { print(i) } }
Optional(1) nil Optional(2) (edited)Iterator の API が hasNext と next に分かれていて、まず hasNext で存在を確認してから next を呼ぶという手順になっていますね。その点、 next だけで済ませられる Swift の設計はシンプルだと思います。Iterator の API が hasNext と next に分かれていて、まず hasNext で存在を確認してから next を呼ぶという手順になっていますね。その点、 next だけで済ませられる Swift の設計はシンプルだと思います。 func graphemeBreakPropertyData( forLine line: String ) -> (scalars: ClosedRange<Unicode.Scalar>, property: Unicode.GraphemeBreakProperty)? { line .match(/([0-9A-F]+)(?:\.\.([0-9A-F]+))?\s*;\s(\w+).*/)? .captures.flatMap { (l, u, p) in guard let property = Unicode.GraphemeBreakProperty(p) else { return nil } let scalars = Unicode.Scalar(hex: l)! ... Unicode.Scalar(hex: u ?? l)! return (scalars, property) } }l, u, p の3要素タプルなのは正規表現パターンと対応しているのか。これはいいなあ。func graphemeBreakPropertyData( forLine line: String ) -> (scalars: ClosedRange<Unicode.Scalar>, property: Unicode.GraphemeBreakProperty)? { line.match { OneOrMore(.hexDigit).capture { Unicode.Scalar(hex: $0) } Optionally { ".." OneOrMore(.hexDigit).capture { Unicode.Scalar(hex: $0) } } OneOrMore(.whitespace) ";" OneOrMore(.whitespace) OneOrMore(.word).capture(GraphemeBreakProperty.init) Repeat(.anyCharacter) }?.captures.map { (lower, upper, property) in let scalars = lower ... (upper ?? lower) return (scalars, property) } }captures.map { が (lower, upper, property) の3要素タプルになってるところで.match の中に書いてある3つの .capture があることと対応しているのか。*がOneOrMoreって書かれてて心配になる。ZeroOrMoreAsyncStreamの挙動を調べていたのですが、(1)のループの後にfinishを呼ぶとonTerminationのコールバックが呼ばれてcancel扱いになるのですが、これってこういうものなのでしょうか? なんとなく「値の出力が全部終わってfinishしたらなぜかcancel処理が走った」みたいな印象を受けたのでどうなのかなあと思いましてfunc hoge() async { let digits = AsyncStream(Int.self) { continuation in Task.detached { for digit in 0..<10 { // (1) continuation.yield(digit) } continuation.finish() continuation.onTermination = { @Sendable finished in if case .cancelled = finished { print("cancel") // 呼ばれる } } } } for await digit in digits { print(digit) } }
https://developer.apple.com/documentation/swift/asyncstream (edited)func hoge() async { let digits = AsyncStream(Int.self) { continuation in Task.detached { for digit in 0..<10 { continuation.yield(digit) } continuation.onTermination = { @Sendable finished in switch finished { case .finished: print("finished") // タイミングがバラバラ case .cancelled: print("cancel") @unknown default: break } } continuation.finish() } } for await digit in digits { print(digit) } } (edited)MainActor )で final class UserViewController: UIViewController { private let state: UserViewState ... override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) state.loadUser() } } としてるの、ダメでは?これだと loadUser の非同期処理が終わるまで viewDidAppear を抜けられない・・・。 https://zenn.dev/koher/articles/swift-concurrency-cheatsheet#%F0%9F%92%BC-case-20-(mainactor)%3A-%E5%85%B1%E6%9C%89%E3%81%95%E3%82%8C%E3%81%9F%E7%8A%B6%E6%85%8B%E3%81%AE%E5%A4%89%E6%9B%B4%EF%BC%88%E3%83%A1%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E4%B8%8A%E3%81%A7%E3%81%AE%E5%87%A6%E7%90%86%EF%BC%89 func loadUser() { Task { do { user = try await fetchUser(for: userID) } catch { // エラーハンドリング } } }@MainActor で意図せず async であるべきメソッドが同期的に呼べてヤバいケースがあるんじゃないかと考えてたんだけど、 fetchUser は @MainActor とは関係のない async 関数だから、ここで await が必須になって、 loadUser に async をつけなきゃとなった時点で気付くか。よかった。 (edited)protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as? () -> P { print("f2 ok") } else { print("f2 ng") }protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as? () -> P { print("f2 ok") } else { print("f2 ng") } f1 ok f2 ngstderr:<stdin>:8:19: warning: runtime conversion from '() -> S' to '() -> P' is not supported; cast always fails if let f2 = makeS as? () -> P { ^ <stdin>:8:19: note: consider using 'as' coercion instead if let f2 = makeS as? () -> P { ^~~ as <stdin>:8:8: warning: value 'f2' was defined but never used; consider replacing with boolean test if let f2 = makeS as? () -> P { ~~~~^~~~~ ~~~ isas は通るけど as? は失敗する、という事があるらしい。protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as Any as? () -> P { print("f2 ok") } else { print("f2 ng") }protocol P {} struct S: P {} func makeS() -> S { S() } let f1 = makeS as () -> P print("f1 ok") if let f2 = makeS as Any as? () -> P { print("f2 ok") } else { print("f2 ng") } f1 ok f2 ngstderr:<stdin>:8:8: warning: value 'f2' was defined but never used; consider replacing with boolean test if let f2 = makeS as Any as? () -> P { ~~~~^~~~~ ~~~ isSwift パッケージに、 CodableConfiguration という新要素が勝手に追加されてる。 https://developer.apple.com/documentation/swift/keyeddecodingcontainer/3766918-decodeCodableConfiguration は Foundation だからセーフかSwift.KeyedDecodingContainer.func decode<T, C>(_: CodableConfiguration<T?, C>.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> CodableConfiguration<T?, C> where T : DecodableWithConfiguration, T : EncodableWithConfiguration, C : DecodingConfigurationProviding, C : EncodingConfigurationProviding, T.DecodingConfiguration == C.DecodingConfiguration, T.EncodingConfiguration == C.EncodingConfigurationKeyedDecodingContainer は struct だから、Foundation から extension で生やす分にはSwift モジュールに影響はないのか。#if 書ける案#if より if 書きたいなと思った。Optional な値で、存在するときだけ Array や Dictionary に入れたいとか。const args = [ 'xcresulttool', 'get', '--path', this.bundlePath, '--format', 'json' ] if (reference) { args.push('--id') args.push(reference) } こういう感じだな。+ と三項演算子でそれっぽく書けるか。。。?.compact() で取り除いてるprotocol P {} extension P { // static funcが書けるようになった static func foo() -> Int { 0 } /* インナータイプは作れないけど struct Inner {} */ } /* typealiasできるので実質可能? */ struct _P_Inner {} extension P { typealias Inner = _P_Inner } print(P.Inner())struct InvalidP: P {} extension P where Self == InvalidP {} とすることで foo を呼べますP.foo() じゃなくて func f(_ p: P) に対して f(.foo()) という形になるので多少ややこしいextension Never: P {} extension P where Self == Never {} のほうが安全だということに気づいた@Bar var bar = 1 コレは通るんじゃないかしら@propertyWrapper struct StringDictionary { var wrappedValue: [String: String] } @StringDictionary var d1. // infers Dictionary<String, String> @StringDictionary var d2: Dictionary // infers <String, String> ↑なんかいろいろ怪しいサンプルコードはある (edited)var x: Int? は右辺値要らないけど・・・@Bar var b が駄目なのが話と違うような気もしてきたなvar a: Voidvar a: Void var b: Intfunc foo() { let a: Void let b: Int }func foo() { let a: Void let b: Int } <stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _func foo() { let a: Void let b: Int }func foo() { let a: Void let b: Int } <stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _func foo() { let a: Void let b: Int } <stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _func foo() { let a: Void let b: Int } <stdin>:2:7: warning: immutable value 'a' was never used; consider replacing with '_' or removing it let a: Void ^ _ <stdin>:3:7: warning: immutable value 'b' was never used; consider replacing with '_' or removing it let b: Int ^ _let a: Void print(a)func foo() { let a: Void let b: Int print(a) print(b) } (edited)func foo() { let a: Void let b: Int print(a) print(b) } (edited)<stdin>:5:9: error: constant 'b' used before being initialized print(b) ^ <stdin>:3:7: note: constant defined here let b: Int ^@Foo var a = () って書くのは変だな= () は何もない時にそう書いてる扱いがされるって感じか@propertyWrapper struct Bar { var wrappedValue: Int init(wrappedValue: Int = 0) { self.wrappedValue = wrappedValue } } struct S { @Bar var b }init(wrappedValue: T? = nil) がいけるのか@Bar<Int> var b これはいけるでしょ (edited)@Environment(\.colorScheme) var colorScheme みたいなonAppear の中はコンパイラ的にはMainActorっぽい(viewModel2のメソッド呼び出しにawaitがいらないので)けど明示的に @MainActor がついてないほうはコンテキストの引き継ぎが行われなくて、メインスレッド警告がでている?-warn-concurrency ついてなかったから気づかなかったけどちゃんと警告でてた・・( Cannot use parameter 'self' with a non-sendable type 'ViewModel1' from concurrently-executed code ) Taskの中でawaitが起こるときにselfが明示的なactor contextを持っているかを確認してあればそのactor contextにスイッチするという感じなのかなself が Sendable じゃないからそもそも Task.init のクロージャに渡せないのか。func ごとに静的に決定されるもので、クロージャの場合、条件を満たせば( non-@Sendable または Task.init )引き継がれる?await の箇所ごとにコンパイラが見える範囲まで最寄りのActor Contextを探して、見つからなかったらそのままasync側のコンテキストに着地してそうasync 側のコンテクストも存在しないのかな。結果的に async API の呼び出し先依存のスレッドで実行されるにしても、そこでさらにクロージャを書いたからといってそのコンテクストが引き継がれるとかはないと。actor Foo { func bar() -> Int { // Thread 2 & Foo の Actor Context 42 } }
let foo = Foo() Task { // Thread 1 let x = await foo.bar() // Thread 2 (ただし Foo の Actor Context ではない) print(x) Task { // Thread 3 ( Foo の Actor Context は引き継がない) print(x) } } (edited)func global() async -> Int { return await withCheckedContinuation { c in DispatchQueue.global().async { c.resume(returning: 42) } } } @MainActor final class Foo { func start() { Task { let x = await global() print(x, Thread.isMainThread) // 42 true Task { let x = await global() print(x, Thread.isMainThread) // 42 true } } } } これは引き継がれてそうですstart が Main Actor の Actor Context を持つので、その中に書かれたクロージャには(条件を満たせば) Actor Context が引き継がれますね。bar を呼び出すのに foo を書き忘れてたので・・・。await bar() → await foo.bar() の修正と、 foo の宣言を最初に追加しました。actor Bar { func bar() -> Int { DispatchQueue.main.sync { 42 } } } final class Foo { func start() { let bar = Bar() Task { let x = await bar.bar() print(x, Thread.isMainThread) // 42 false Task { let x = await bar.bar() print(x, Thread.isMainThread) // 42 false } } } } これはfalseだ。async元のコンテキストを引き継いでるのではなく無のコンテキストにスイッチしてるのかな@MainActor final class Bar { func bar() -> Int { 42 } } final class Foo { func start(bar: Bar) { Task { print(Thread.isMainThread) // 42 false let x = await bar.bar() print(x, Thread.isMainThread) // 42 true Task { print(Thread.isMainThread) // 42 false let x = await bar.bar() print(x, Thread.isMainThread) // 42 true } } } } (edited)Call to main actor-isolated initializer 'init()' in a synchronous nonisolated contextBar がinit()できないですね・・Foo は actor になってないですよね?Task.init 自体がコンテクストを持つのかな?import SwiftUI @MainActor struct ContentView: View { let bar = Bar() var body: some View { Text("Hello, world!") .onAppear { Foo().start(bar: bar) } } } @MainActor final class Bar { func bar() -> Int { print("bar", Thread.isMainThread) return 42 } } final class Foo { func start(bar: Bar) { Task { print("1", Thread.isMainThread, ObjectIdentifier(Thread.current)) let x = await bar.bar() print("2", Thread.isMainThread, ObjectIdentifier(Thread.current), x) Task { print("3", Thread.isMainThread, ObjectIdentifier(Thread.current)) let x = await bar.bar() print("4", Thread.isMainThread, ObjectIdentifier(Thread.current), x) Task.detached { print("5", Thread.isMainThread, ObjectIdentifier(Thread.current)) let x = await bar.bar() print("6", Thread.isMainThread, ObjectIdentifier(Thread.current), x) } } } } }
1 false ObjectIdentifier(0x0000600001348780) bar true 2 false ObjectIdentifier(0x0000600001319580) 42 3 false ObjectIdentifier(0x0000600001319580) bar true 4 false ObjectIdentifier(0x0000600001348780) 42 5 false ObjectIdentifier(0x0000600001319580) bar true 6 false ObjectIdentifier(0x0000600001348780) 42bar はたしかにメインスレッドで実行されているのとTask は Actor Context を持つわけではなさそう。 Actor Context がない場合も await 先が結果を返したスレッドでそのまま実行されるわけではなく、また別のスレッドに投入される?await の後は同期的だと思ってましたけど、それは違うと(どのスレッドで実行されるかわからない)。 Actor Context は静的に解決されると考えて、それ以外に想定外なところはなさそう。@usableFromInline internal struct S { @usableFromInline internal var a: Int @inlinable internal init() { self.a = 0 } }a.swift:8:16: error: 'self' used before 'self.init' call or assignment to 'self' self.a = 0 ^ a.swift:9:5: error: 'self.init' isn't called on all paths before returning from initializer } ^struct S を @frozen にするとビルドできるようになるんだけどinit が @inlinable だと、レイアウト変化後に、埋め込まれた古い init が正しく動作しないからおかしい」 というのが根底の話でTask.yield()使ってみたらうまくいけたんですけれど、合法的な使い方なのか怪しい… async let result: () = viewModel.fetchRepository() await Task.yield() XCTAssertEqual(viewModel.repositories, []) stubApiClient.continue() // resumeで値を返却させる await result XCTAssertEqual(viewModel.repositories.count, 1)func fetchRepositories(userName: String) async throws -> [FooItem] { // ここで値を返すとawait中の検証ができない return try await withCheckedThrowingContinuation { continuation in self.continuation = continuation } } func `continue`() { self.continuation.resume(returning: expectResult) }Task.yield() がない場合、await内の処理がまったく進まないので、上のコードだと支障ないんですけれど、内部で値を変えているものが未反映になる感じですねXCTAssertFalse(viewModel.showProgress) async let result: () = viewModel.fetchRepository() await Task.yield() XCTAssertTrue(viewModel.showProgress) // ここを検証したい stubApiClient.continue() // resumeで値を返却させる await result XCTAssertFalse(viewModel.showProgress)viewModel.fetchRepository の処理が動きはじめて viewModel.showPregress になったが、stubAPIClient.continue の呼び出しがまだされていない 時刻で、状態をassertしたいが、async let の後に Task.yield が無い場合、 viewModel.fetchRepository の処理がまだ開始していない時刻に assert が処理してしまうってことですよね?viewModel.fetchRepository の処理が動き始めるのを待機する」ロジックを明確に書くのが良いと思います。viewModel.fetchRepository の中で、 StubAPIClient.fetchRepository を呼ぶようになってますよね、なので、StubAPIClient に、 fetchRepository が呼ばれたら返ってくる await property を生やすのが良いんじゃないでしょうかXCTAssertFalse(viewModel.showProgress) async let result: () = viewModel.fetchRepository() await stubAPIClient.willFetchRepository XCTAssertTrue(viewModel.showProgress) stubApiClient.continue() await result XCTAssertFalse(viewModel.showProgress)Task.yield 1回だと期待した状態まで進行しない懸念も消せます (edited)withCString(:_) とか withUnsafeBytes(:_) をリストで扱いたいと思って、つまり let array: [String] = [...] array.with(each: String.withCString) { cStrings/*: [UnsafePointer<CChar>] */ in cFunctionReceivingCStringArray(cStrings) } こういうのがやりたくて、邪悪なものを生み出してしまった。 extension BidirectionalCollection { func with<U, R>( each mapFn: (Element) -> ((U) throws -> R) throws -> R, _ fn: ([U]) throws -> R ) rethrows -> R { try withoutActuallyEscaping(fn) { fn in try withoutActuallyEscaping(mapFn) { mapFn in var stash: [U] = [] stash.reserveCapacity(count) let closure = reversed().reduce({ try fn(stash) }) { closure, elem in { try mapFn(elem)() { val in stash.append(val) return try closure() } } } return try closure() } } } } (edited)@available(macOS 12.3, iOS 15.4, watchOS 8.5, tvOS 15.4, *) public protocol CodingKeyRepresentable となっているものの適合時に@available付けなくても何も言われず、下位バージョンだと以前のままArray扱いになってしまうみたいですね…main に取り込まれたんじゃなかったっけ?マニフェストが出ただけだっけ?main に取り込まれたんじゃなかったっけ?マニフェストが出ただけだっけ? import _Differentiation で使える状態っぽい。 プロポーザルは下書きしか存在しないはずget/setの組で操作せずに _modify に出来ると回避できるんですよね
1import Foundation let arraySize = 1000000 let loopCount = 1000 class Values { var values = [Int](repeating: 0, count: arraySize) func foo1() { values[0] += values[0] } func foo2() { let v = values[0] values[0] += v } } func measure(_ body: () -> Void) { let time1 = Date.timeIntervalSinceReferenceDate for _ in 0 ..< loopCount { body() } let time2 = Date.timeIntervalSinceReferenceDate print(time2 - time1) } let values = Values() measure { values.foo1() } measure { values.foo2() }import Foundation let arraySize = 1000000 let loopCount = 1000 class Values { var values = [Int](repeating: 0, count: arraySize) func foo1() { values[0] += values[0] } func foo2() { let v = values[0] values[0] += v } } func measure(_ body: () -> Void) { let time1 = Date.timeIntervalSinceReferenceDate for _ in 0 ..< loopCount { body() } let time2 = Date.timeIntervalSinceReferenceDate print(time2 - time1) } let values = Values() measure { values.foo1() } measure { values.foo2() } 0.9675040245056152 0.00011301040649414062Values をstructにしても起こりません。@escaping な non-@Sendable クロージャは actor context を引き継ぐという理解なんですけど、↓の foo が返すクロージャは常にメインスレッドで実行される( true が表示される)ということでしょうか? @MainActor func foo() -> () -> Void { // main actor context { // actor context を引き継ぐ print(Thread.isMainThread) } } この foo で返されたクロージャを Task.detached から呼び出すと false が表示されたんですが、これは僕の仕様理解が間違っているか、バグか、未実装か、実行方法に問題がある(何かオプションを付けないといけないなど)か、どう考えれば良いでしょうか? (edited)async でないからメインスレッドで同期実行というのが破綻してるか。DispatchQueue.global().async みたいな API に渡すケースは今は穴が空いているけど、将来的にはそういう穴は塞がれる( DispatchQueue.global().async のような API が廃止される?)みたいな話をしたと思うんですが、↑のようなケースはどうなるんでしょう? foo がコンパイルエラー? func foo() -> @MainActor () -> Void じゃないといけない?func bar() { // StrictConcurrencyChecking: Minimal DispatchQueue.global().async { await foo()() // ❌ Cannot pass function of type '() async -> Void' to parameter expecting synchronous function type } // StrictConcurrencyChecking: Complete DispatchQueue.global().async { await foo()() // ❌ Cannot pass function of type '@Sendable () async -> Void' to parameter expecting synchronous function type } } (edited)@escaping クロージャの actor context 引き継ぎとは、何(誰)の context を引き継ぐのでしょうか?
@Sendable cannot escape the concurrency domain in which it was formed. @MainActor で escape できるのがおかしい? @swift-5.6.3
func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } actor Foo { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } }@MainActor で escape できるのがおかしい? @swift-5.6.3
func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } actor Foo { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } } <stdin>:18:24: error: actor-isolated property 'count' can not be referenced from a non-isolated context print(self.count) ^ <stdin>:10:9: note: property declared here var count: Int = 0 ^func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } @MainActor final class Bar { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } }func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } @MainActor final class Bar { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) } } } func runEscaping(_ operation: @escaping () -> Void) { Task.detached { operation() } } うろ覚えなんですが、Concurrencyの機能が使われていない場合、Sendableを削除してませんでしたっけ?なのでMainActorの方のcountはエラーにならない? // Strip off Sendable and (possibly) the global actor.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4546 asyncとSendableを使っている場合はConcurrencyを使っているとみなされる。 // Async and @Sendable closures use concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4001 actorの内部にある場合もConcurrencyを使っているとみなされる。 // If we're in an actor, we're using concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4027 @Sendable付けると@MainActor Barの方もエラーになるんですよねえ func runEscaping(_ operation: @Sendable @escaping () -> Void) { operation() }
(edited)func runEscaping(_ operation: @escaping () -> Void) { Task.detached { operation() } } うろ覚えなんですが、Concurrencyの機能が使われていない場合、Sendableを削除してませんでしたっけ?なのでMainActorの方のcountはエラーにならない? // Strip off Sendable and (possibly) the global actor.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4546 asyncとSendableを使っている場合はConcurrencyを使っているとみなされる。 // Async and @Sendable closures use concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4001 actorの内部にある場合もConcurrencyを使っているとみなされる。 // If we're in an actor, we're using concurrency features.
https://github.com/apple/swift/blob/5c05da0a1d8dad5bd945918ce9e24603091b88c5/lib/Sema/TypeCheckConcurrency.cpp#L4027 @Sendable付けると@MainActor Barの方もエラーになるんですよねえ func runEscaping(_ operation: @Sendable @escaping () -> Void) { operation() }
(edited)runEscaping がどのように実装されているかわからないと思うんですが、 runEscaping がブラックボックスだと考えたときに @MainActor func foo() { // main actor context runEscaping { // ここに main actor context が引き継がれることが保証されていないと何も信じられない } } となりませんか?だから、 actor context の引き継ぎはコールスタックがどのようになっていてもクロージャの宣言箇所によって決められる(たとえ Task.detached を挟もうが、 main actor context を引き継ぐ箇所で宣言されたクロージャは実行時に再度 main queue に投入される)のだと思っていました。 ただ、普通の actor だとコンパイルエラーになっていることから、 MainActor (というか global actor ?)の挙動がおかしいんでしょうか?
(edited)// ここに main actor context が引き継がれることが保証されていないと何も信じられない
runEscapingは func runEscaping(_ operation: @escaping () -> Void) { Task.detached { // actorのcontextは引き継がないのでmain actor contextではない operation() } } とコンパイラが判定していると思ってました。 あと、今再確認したら↓はSwift5.7だとエラーになりますね(昨日はなんかキャッシュが残っていたようです) func run(_ operation: () -> Void) { operation() } func runEscaping(_ operation: @escaping () -> Void) { operation() } @MainActor final class Bar { var count: Int = 0 func countUp() { run { count += 1 } runEscaping { [weak self] in guard let self = self else { return } print(self.count) // :x: Property 'count' isolated to global actor 'MainActor' can not be referenced from a non-isolated synchronous context } } } (edited)@escaping な non-@Sendable クロージャは actor context を引き継ぐという理解なんですけど、↓の foo が返すクロージャは常にメインスレッドで実行される( true が表示される)ということでしょうか? @MainActor func foo() -> () -> Void { // main actor context { // actor context を引き継ぐ print(Thread.isMainThread) } } この foo で返されたクロージャを Task.detached から呼び出すと false が表示されたんですが、これは僕の仕様理解が間違っているか、バグか、未実装か、実行方法に問題がある(何かオプションを付けないといけないなど)か、どう考えれば良いでしょうか? (edited)
1import Foundation @MainActor func foo() -> () -> Void { { print(Thread.isMainThread) } } @main struct Main { @MainActor static func main() async { let f = foo() let t = Task.detached { f() } _ = await t.value } } (edited)import Foundation @MainActor func foo() -> () -> Void { { print(Thread.isMainThread) } } @main struct Main { @MainActor static func main() async { let f = foo() let t = Task.detached { f() } _ = await t.value } } (edited)falsestderr:<stdin>:16:7: warning: capture of 'f' with non-sendable type '() -> Void' in a `@Sendable` closure f() ^ <stdin>:16:7: note: a function type must be marked '@Sendable' to conform to 'Sendable' f() ^ (edited)func runEscaping(_ operation: @escaping () -> Void) { Task.detached { // Taskも同じ結果 print("3", Thread.isMainThread) // false operation() } } @MainActor final class Bar { func countUp() { print("1", Thread.isMainThread) // true runEscaping { print("2", Thread.isMainThread) // false } } } ↓はWWDCのセッションで言われていた通り、nonisolatedなsync関数は呼び出し側のcontextに依る(そもそもescapingしないのか) func runEscaping(_ operation: @escaping () -> Void) { print("3", Thread.isMainThread) // true operation() } @MainActor final class Bar { func countUp() { print("1", Thread.isMainThread) // true runEscaping { print("2", Thread.isMainThread) // true } } }
(edited)func runEscaping(_ operation: @escaping () -> Void) { Task.detached { print("3", Thread.isMainThread) // false operation() } } @MainActor final class Bar { func countUp() { print("1", Thread.isMainThread) // true runEscaping { print("2", Thread.isMainThread) // false Task { // ここはmain actor contextが引き継がれる print("4", Thread.isMainThread) // true👀 } } } } asyncを足すとtrueになる(MainActorにhopできる) func runEscaping(operation: @escaping () async -> Void) { Task.detached { print("3", Thread.isMainThread) // false await operation() } } @MainActor final class Bar { func countUp() { runEscaping { print("2", Thread.isMainThread) // trueになる👀 } } } (edited)public struct _NSRange { public var location: Int public var length: Int public init() { location = 0 length = 0 } public init(location: Int, length: Int) { self.location = location self.length = length } }
https://github.com/apple/swift-corelibs-foundation/blob/ee856f110177289af602c4040a996507f7d1b3ce/Sources/Foundation/NSRange.swiftextension _NSRange: @unchecked Sendable {} はどうですか?@uncheckedつけると黙らせられるな。なんか気持ち悪いけど、まぁ今回のケースは実際安全なことがわかってるからとりあえずはこれでもいいか。ありがとう。@unchecked を使いなさい」って感じですけど、 この理由はおそらく、別のファイルで Sendable 準拠すると、定義元のライブラリがソースアップデートする場合には(本当にスレッドアンセーフなデグレ改修がされて)ソース互換性が壊れる可能性があるし、バイナリアップデートする場合には嘘のSendableになってしまうからですね。 @unchecked はそのように後に壊れる可能性がある事を許容する宣言でもある。 まさに「実際安全な事がわかってる間」にしのぐための言語仕様です。 (edited)@uncheckedをつけることになるのでその場しのぎじゃなくても普通に使いませんか?class の実装で内部は DispatchQueue とかで自力で安全を保障する場合はそうですね。@unchecked が気持ち悪いとか実装で保証してることとの区別がつかなくて嫌なときは @preconcurrency import struct Foundation.NSRange と書くのもいいかもしれないimport Foundation @preconcurrency import struct Foundation.NSRange func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } }import Foundation @preconcurrency import struct Foundation.NSRange func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } } <stdin>:8:15: warning: capture of 'range' with non-sendable type 'NSRange' (aka '_NSRange') in a `@Sendable` closure print(range, string) ^ Foundation._NSRange:1:15: note: struct '_NSRange' does not conform to the 'Sendable' protocol public struct _NSRange { ^ <stdin>:1:1: remark: add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Foundation' import Foundation ^ @preconcurrency <stdin>:8:22: warning: capture of 'string' with non-sendable type 'NSString' in a `@Sendable` closure print(range, string) ^ Foundation.NSString:1:12: note: class 'NSString' does not conform to the 'Sendable' protocol open class NSString : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCoding { ^ <stdin>:2:17: remark: '@preconcurrency' attribute on module 'Foundation' is unused @preconcurrency import struct Foundation.NSRange ~~~~~~~~~~~~~~~~^@preconcurrency import struct Foundation.NSRange import Foundation func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } }@preconcurrency import struct Foundation.NSRange import Foundation func f() { let range = NSRange.init() let string = NSString.init() Task { print(range, string) } } @rethrows って、いつ正式に Swift に入ったんですっけ? @rethrows protocol AsyncSequence
https://developer.apple.com/documentation/Swift/AsyncSequence ↓にも追加されてなさそうだし、 https://docs.swift.org/swift-book/ReferenceManual/Attributes.html ↓ AsyncSequence の Proposal にも書かれてなさそう。 https://github.com/apple/swift-evolution/blob/main/proposals/0298-asyncsequence.md@_ じゃない? _ なくてびっくりした。@rethrows が付くのか_ が無いんだろうか?AsyncSequenceProtocol にも @rethrows 付いてるな。 https://developer.apple.com/documentation/swift/asynciteratorprotocol@rethrows が付くのか @rethrows と rethrows は直接的にキーワードとして結びつくのかな? @Sendable と Sendable みたいな意味合いでってこと? @Sendable は Proposal 通ってるしなぁ。 Marker Protocol と @ の関係が先送りされただけで。func next() rethrows によって機能するらしいけど@rethrows が何をやりたいのかはわかるにしても、どう働く仕様なのか何の資料もなくてわからない・・・。@rethrows の影響をうけてどう変化するのかがわからんAsyncIteratorProtocol の方は唯一の throws を持つメソッドがあるからわからんでもないけど、 AsyncSequence はさらに謎じゃない?Rethrows protocol って表現されてるプロトコルに @rethrows が付いてそう@available(SwiftStdlib 5.1, *) @rethrows public protocol AsyncSequence { /// The type of asynchronous iterator that produces elements of this /// asynchronous sequence. associatedtype AsyncIterator: AsyncIteratorProtocol where AsyncIterator.Element == Element /// The type of element produced by this asynchronous sequence. associatedtype Element /// Creates the asynchronous iterator that produces elements of this /// asynchronous sequence. /// /// - Returns: An instance of the `AsyncIterator` type used to produce /// elements of the asynchronous sequence. __consuming func makeAsyncIterator() -> AsyncIterator }@rethrows だから、throws性が伝搬するって決まるの?@available(SwiftStdlib 5.1, *) @rethrows public protocol AsyncIteratorProtocol { associatedtype Element /// Asynchronously advances to the next element and returns it, or ends the /// sequence if there is no next element. /// /// - Returns: The next element, if it exists, or `nil` to signal the end of /// the sequence. mutating func next() async throws -> Element? } ↑さらに、rethrowsじゃなくてthrowsって書いてあるんだけど・・・ (edited)
1@rethrows protocol における requirements に書かれた throws に影響があるとしたらAsyncIteratorProtocol は throws を持つ唯一の required なメソッドを持つからそれに @rethrows が働いていると考えられるけど、AsyncSequence への伝播が謎。norethrows とかで抑制するとか?@rethrows 付与されてるだけで解釈されてなくて、 AsyncSequence と AsyncIteratorProtocol 以外では働かない可能性ありそう。for try await in が for await in にできるだけみたいな。@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() }@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() } protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() }protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) async throws -> Int? { try iter.next() } @rethrows 関係ない気が。@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } struct FailableIter: RethrowsIterator { func next() throws -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) rethrows -> Int? { try iter.next() } var it = NonfailableIter() _ = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2) (edited)@rethrows protocol RethrowsIterator { func next() throws -> Int? } struct NonfailableIter: RethrowsIterator { func next() -> Int? { return 1 } } struct FailableIter: RethrowsIterator { func next() throws -> Int? { return 1 } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) rethrows -> Int? { try iter.next() } var it = NonfailableIter() _ = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2) (edited)Result<T, Never> にするのと同じことが throws に対してできるprotocol RethrowsIterator { associatedtype Failure: Error func next() -> Result<Int?, Failure> } struct NonfailableIter: RethrowsIterator { func next() -> Result<Int?, Never> { return .success(1) } } struct FailableIter: RethrowsIterator { func next() -> Result<Int?, Error> { return .success(1) } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) -> Result<Int?, T.Failure> { iter.next() } var it = NonfailableIter() let _: Result<Int?, Never> = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2).get() (edited)protocol RethrowsIterator { associatedtype Failure: Error func next() -> Result<Int?, Failure> } struct NonfailableIter: RethrowsIterator { func next() -> Result<Int?, Never> { return .success(1) } } struct FailableIter: RethrowsIterator { func next() -> Result<Int?, Error> { return .success(1) } } func callNextRethrows<T: RethrowsIterator>(_ iter: inout T) -> Result<Int?, T.Failure> { iter.next() } var it = NonfailableIter() let _: Result<Int?, Never> = callNextRethrows(&it) var it2 = FailableIter() _ = try callNextRethrows(&it2).get() (edited)inout とか除いて単純にするとこうか。 @swift-5.7.3
@rethrows protocol P { func foo() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } func callFoo<T: P>(of p: T) rethrows { try p.foo() } callFoo(of: S1()) do { try callFoo(of: S2()) } catch { print(error) }inout とか除いて単純にするとこうか。 @swift-5.7.3
@rethrows protocol P { func foo() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } func callFoo<T: P>(of p: T) rethrows { try p.foo() } callFoo(of: S1()) do { try callFoo(of: S2()) } catch { print(error) } S1 S2@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) } S1 S2throws を持つメソッドが複数あった場合、両方に働きそう。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() throws { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }throws を持つメソッドが複数あった場合、両方に働きそう。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() throws { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) } S1.foo S2.foothrows を互い違いに付けた場合。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() throws { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) }throws を互い違いに付けた場合。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() throws { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) } <stdin>:49:1: error: call can throw but is not marked with 'try' callFoo(from: T1()) ^~~~~~~~~~~~~~~~~~~ <stdin>:49:1: note: call is to 'rethrows' function, but a conformance has a throwing witness callFoo(from: T1()) ^throws を互い違いに付けた場合。 @swift-5.7.3
@rethrows protocol P { func foo() throws func bar() throws } @rethrows protocol Q { associatedtype PType: P func makeP() -> PType } struct S1: P { func foo() { print("S1.foo") } func bar() throws { print("S1.bar") } } struct S2: P { func foo() throws { print("S2.foo") } func bar() { print("S2.bar") } } struct T1: Q { func makeP() -> S1 { S1() } } struct T2: Q { func makeP() -> S2 { S2() } } func callFoo<T: Q>(from q: T) rethrows { try q.makeP().foo() } callFoo(from: T1()) do { try callFoo(from: T2()) } catch { print(error) } callFoo の宣言がrethrowsになってて、普通なら一緒に渡してる引数のクロージャがthrowsがどうかが伝搬するけど、ここでは引数のrethrowsプロトコルがどうか、になるんですね (edited)@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType1: P associatedtype PType2: P func makeP1() -> PType1 func makeP2() -> PType2 } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP1() -> S1 { S1() } func makeP2() -> S2 { S2() } } struct T2: Q { func makeP1() -> S2 { S2() } func makeP2() -> S1 { S1() } } func callFooOfP1<T: Q>(from q: T) rethrows { try q.makeP1().foo() } callFooOfP1(from: T1()) do { try callFooOfP1(from: T2()) } catch { print(error) }@rethrows protocol P { func foo() throws } @rethrows protocol Q { associatedtype PType1: P associatedtype PType2: P func makeP1() -> PType1 func makeP2() -> PType2 } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T1: Q { func makeP1() -> S1 { S1() } func makeP2() -> S2 { S2() } } struct T2: Q { func makeP1() -> S2 { S2() } func makeP2() -> S1 { S1() } } func callFooOfP1<T: Q>(from q: T) rethrows { try q.makeP1().foo() } callFooOfP1(from: T1()) do { try callFooOfP1(from: T2()) } catch { print(error) } <stdin>:48:1: error: call can throw but is not marked with 'try' callFooOfP1(from: T1()) ^~~~~~~~~~~~~~~~~~~~~~~ <stdin>:48:1: note: call is to 'rethrows' function, but a conformance has a throwing witness callFooOfP1(from: T1()) ^@rethrows が付与されたプロトコルを @rethrows プロトコルと呼ぶことにする。また、 P が @rethrows プロトコルのとき 、 func foo<T: P>(_ x: T) rethrows が throws になる場合、 T が rethrowing であると呼ぶことにする。 このとき、 T が non-rethrowing である条件は、下記の二つが同時に満たされる場合に限る。 P のメソッドの内、 throws が付与されたものをすべて non-throws なメソッドとして実装するP の associatedtype の内、 @rethrows プロトコルを制約として持つものをすべて non-rethrowing な型で実装する@rethrows protocol P { func foo() throws } @rethrows protocol Q { func bar() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T<PType: P>: Q { let p: PType func bar() rethrows { try p.foo() } } T(p: S1()).bar() do { try T(p: S2()).bar() } catch { print(error) }@rethrows protocol P { func foo() throws } @rethrows protocol Q { func bar() throws } struct S1: P { func foo() { print("S1") } } struct S2: P { func foo() throws { print("S2") } } struct T<PType: P>: Q { let p: PType func bar() rethrows { try p.foo() } } T(p: S1()).bar() do { try T(p: S2()).bar() } catch { print(error) } S1 S2@rethrows 使って rethrows できる。@rethrows protocol Error { func throw() throws -> Never } (edited)@rethrows protocol FailureProtocol: Error { func throwSelf() throws -> Never } extension FailureProtocol { func throwSelf() throws -> Never { throw self } } extension Never: FailureProtocol { func throwSelf() -> Never { fatalError() } } struct FooError: FailureProtocol {} @rethrows protocol ResultProtocol { associatedtype Value associatedtype Failure: FailureProtocol func get() throws -> Value } enum Result<Value, Failure: FailureProtocol>: ResultProtocol { case success(Value) case failure(Failure) func get() rethrows -> Value { switch self { case .success(let value): return value case .failure(let error): try error.throwSelf() } } } func useResult<R: ResultProtocol>(_ result: R) rethrows { try print(result.get()) } useResult(Result<Int, Never>.success(42)) try useResult(Result<Int, FooError>.success(42))Publisher が AsyncSequence でないのはなんでだろう?と考えていて、 values を挟んで AsyncPublisher と AsyncThrowingPublisher が必要になるのは↑が欠けているのが問題なんじゃないかなと。@reasync があれば AsyncSequnce も Sequence に統合可能になる?async な関数を async な関数として渡すときは大丈夫なの?暗黙の変換がされている?async な関数を async な関数として渡すときは大丈夫なの?暗黙の変換がされている? func asyncF(_ item: Int, transform: (Int) async -> String)) async -> String { let out = await transform(item) print(out) return out } が、気持ち的にはこんな感じなんですよね↓ func asyncF(_ item: Int, transform: (Int) async -> String), nextContinuation: () -> Void) async { return transform(item, { out in print(out) return nextContinuation(out) }) } (edited)func asyncF(_ item: Int, transform: (Int) async -> String), nextContinuation: () -> Void) async -> String [sync] func asyncF(_ item: Int, transform: (Int) -> String) -> String { var out: String! asyncF(item, sync_to_async(transform), { out = $0 }) return out } (edited)Dictionary.Keys に Set 的なメソッド( intersection, subtracting 等)ほしくないですか?一度 Set に包み直すのはオーバーヘッドがあるし、 Dictionary も Set もハッシュテーブルなので原理的には同等のパフォーマンスで実現できるかと。keys が OrderedSet なのでそういうことできそうなOrderedDictionary の keys が O(1) じゃないんじゃないかという心配が・・・OrderedDictionary の keys が O(1) じゃないんじゃないかという心配が・・・ OrderedSet で持っとくのか。NSDictionary のときに O(n) になってたんですが、それも O(1) になる PR を昔出しました。Clock ってmacOS Venturaにならないと無いんでしたっけCannot find type 'Clock' in scope 言われちゃうんですよね@available(SwiftStdlib 5.7, *) public protocol Clock: Sendable { associatedtype Instant: InstantProtocol var now: Instant { get } var minimumResolution: Instant.Duration { get } func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws }Xxx is available from mac OS 13 みたいな言い方されてた気がしますが、それすら出ない'Clock' is only available in iOS 16.0 or newer になった[omochi@omochi-iMacPro Contents]$ find . | grep libswiftCore.dylib ./Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/swift/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/appletvsimulator/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/watchos/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/iphoneos/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/appletvos/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/watchsimulator/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/macosx/libswiftCore.dylib ./Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/iphonesimulator/libswiftCore.dylib [omochi@omochi-iMacPro Contents]$ pwd /Applications/Xcode14.app/Contentsswift-5.0/macos/libswiftCore.dylib はあった。(この -5.0 ってなんだ?-5.0 ちゃんと知らないThat is to say, instead of # # @available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) # # we can write # # @available(SwiftStdlib 5.2, *) SwiftStdlib 5.7:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0
https://github.com/apple/swift/blob/475ed80c3eedb060d617fceed74686a4c6c86990/utils/availability-macros.deffunc get(id: Int)->String { "" } var myID: String { get(id: 1) // error: expected '{' to start getter definition } (edited)func get(id: Int)->String { "" } var myID: String { get(id: 1) // error: expected '{' to start getter definition } (edited)<stdin>:6:8: error: expected '{' to start getter definition get(id: 1) // error: expected '{' to start getter definition ^ (edited)func get(id: Int)->String { "" } var myID: String { `get`(id: 1) // error: expected '{' to start getter definition }func get(id: Int)->String { "" } var myID: String { `get`(id: 1) // error: expected '{' to start getter definition } protocol P {} struct S: P {} extension CustomStringConvertible { func f() -> some P { S() } } var foo: some P { "".f() } var bar: some P { ("" as any CustomStringConvertible).f() }protocol P {} struct S: P {} extension CustomStringConvertible { func f() -> some P { S() } } var foo: some P { "".f() } var bar: some P { ("" as any CustomStringConvertible).f() } <stdin>:15:39: error: type 'any P' cannot conform to 'P' ("" as any CustomStringConvertible).f() ^ <stdin>:15:39: note: only concrete types such as structs, enums and classes can conform to protocols ("" as any CustomStringConvertible).f() ^ <stdin>:14:10: note: required by opaque return type of var 'bar' var bar: some P { ^~~~~~Error にしても変わりませんでしたprotocol P {} struct S: P {} extension Error { func f() -> some P { S() } } extension String: Error {} var foo: some P { "".f() } var bar: some P { ("" as any Error).f() } (edited)protocol P {} struct S: P {} extension Error { func f() -> some P { S() } } extension String: Error {} var foo: some P { "".f() } var bar: some P { ("" as any Error).f() } (edited)<stdin>:17:21: error: type 'any P' cannot conform to 'P' ("" as any Error).f() ^ <stdin>:17:21: note: only concrete types such as structs, enums and classes can conform to protocols ("" as any Error).f() ^ <stdin>:16:10: note: required by opaque return type of var 'bar' var bar: some P { ^~~~~~ (edited)protocol P {} struct S: P {} extension Error { func f() -> some P { S() } } extension String: Error {} var foo: some P { "".f() } var bar: some P { ("" as any Error).f() } (edited)<stdin>:17:7: error: member 'f' cannot be used on value of protocol type 'Error'; use a generic constraint instead ("" as any Error).f() ~~~~^~~~~~~~~~~~~ ~Self に依存しうるから、一律で禁止されているという感じでしょうかね?struct S<T>: P {} extension CustomErrorConvertible { func foo() -> some P { return S<Self>() } }var bar: some P の真の型がどうなるかが CustomStringConvertible を open した self によって動的に変わるから事前に決定できない (edited)P に生えてるものは全部 any P に生えてる扱いなんですよprotocol P {} struct S: P {} extension Error { func f() -> some P { S() } }protocol P {} struct S: P {} extension Error { func f() -> some P { S() } } protocol P {} struct S: P {} extension Error { func f() -> some P { S() } } protocol P {} struct S: P {} extension Error { func f() -> some P { S() } } extension P は常に <Self where Self. P> に対するextensionなんですよねany P が implicit openによって <T: P> も使えてるってのが真実 (edited)extension any P だったら固定で真の型が返せるね。 (edited)extension P { ... } // ↓第一の心の目: ジェネリックなセマンティクスを正しくみつめる extension <Self: P> {} // ↓第二の心の目: self引数を opaque parameter type的に捉える事ができる extension some P { } // ↓第三の心の目: Swift 7 で `P` を `any P` から `some P` に切り替えるので extension P { ... } // もどった! (edited)protocol P {} protocol Q { func p() -> some P } func foo(q: Q) -> some P { return q.p() }protocol P {} protocol Q { func p() -> some P } func foo(q: Q) -> some P { return q.p() } <stdin>:3:15: error: 'some' type cannot be the return type of a protocol requirement; did you mean to add an associated type? func p() -> some P ^~~~~~ <#AssocType#>protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() }protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() } <stdin>:8:10: error: member 'p' cannot be used on value of protocol type 'Q'; use a generic constraint instead return q.p() ^ ~any Q の some P を返すエントリは呼び出せなかったのか〜protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() }protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() } <stdin>:8:10: error: member 'p' cannot be used on value of protocol type 'Q'; use a generic constraint instead return q.p() ^ ~protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> some P { return q.p() } <stdin>:8:12: error: type 'any P' cannot conform to 'P' return q.p() ^ <stdin>:8:12: note: only concrete types such as structs, enums and classes can conform to protocols return q.p() ^ <stdin>:7:19: note: required by opaque return type of global function 'foo(q:)' func foo(q: Q) -> some P { ^~~~~~protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> any P { return q.p() // erase to any P } (edited)protocol P {} protocol Q {} struct S: P {} extension Q { func p() -> some P { return S() } } func foo(q: Q) -> any P { return q.p() // erase to any P } (edited)any P のanyキーワードのプロポーザルが出たのが去年の12月、引数の some P が2月で、 自分の場合1年間ぐらい予習期間があったっぽいな (edited)some と any 中心の話というより、 Primary Associated Type と some, any 込みで Sequence や Collection を題材にもう一度 POP を考えてみましょうって話でした。some と any 中心の話というより、 Primary Associated Type と some, any 込みで Sequence や Collection を題材にもう一度 POP を考えてみましょうって話でした。 final つければObjCから見えなくなるって書いてあるので、Swiftの機能が使えたりしそう?a non-category @objcImplementation extension ってそういうことか
2@objc を付けるなどのオプトインの作業が必要だったけど、SwiftをC++から呼び出す機能の設計にあたっては、 C++コンパイラが Swift Calling Conventionをサポートする事によって、オプトインの作業無しで、全てのSwift moduleを直接C++から使えるようにするらしい。すごい。
__stdcall みたいな、関数にattributeを付ける事によって、呼び出し規約を指定する機能があるんだけど、これに __swiftcc を追加するって事だと思う。 で、 __swiftcc が付いたC++で書かれたブリッジヘッダーをSwiftコンパイラ側が生成するんだろう。 (edited)swiftcc 追加されたりしてるから、 clang にそれを追加するのもいけるって感じなんじゃないかなあ。SwiftコアチームにはC++コンパイラの人も居るし。 あと、家庭くんがSwiftをWASMにコンパイルするために、WASMのバイナリフォーマットにRelative Pointerを追加してもらった件とかはちょっと似てると思う。 ただ、Visual Studioに搭載されてるMS製のC++コンパイラはどうするんだろうね。c++ CLANG_MACRO("SWIFT_CALL", , "__attribute__((swiftcall))")
https://github.com/apple/swift/blob/8d548efbb981666297077658a745db3bffb35e2d/include/swift/PrintAsClang/ClangMacros.defc++ CLANG_MACRO("SWIFT_INDIRECT_RESULT", , "__attribute__((swift_indirect_result))") CLANG_MACRO("SWIFT_CONTEXT", , "__attribute__((swift_context))") CLANG_MACRO("SWIFT_ERROR_RESULT", , "__attribute__((swift_error_result))") ↑このへんもあった。indirect_result とかもポートしてるの面白いねSIMD4(repeating:) を多用してるのは気になる。SIMD3 にして質量を別で扱うと速くなるかも。 SIMD4 にしてる利点がない気がする。-Ounchecked でコンパイルされてるみたいだから、普通に考えたら Specialize はされそう。swiftc nbody.swift-9.swift -Ounchecked -wmo -o nbody.swift-9.swift_rundot を自前なのやめて import simd したら倍くらいの速度になりました ❯ time ./test 20000000 -0.169075164 -0.169031665 ./test 20000000 2.19s user 0.00s system 99% cpu 2.200 total ❯ time ./test2 20000000 -0.169075164 -0.169031665 ./test2 20000000 1.20s user 0.00s system 99% cpu 1.207 total❯ diff test.swift test2.swift 1a2 > import simd 8,14c9,15 < func dot<V : SIMD>(_ a: V, _ b: V) -> V.Scalar where V.Scalar : FloatingPoint { < var total = V.Scalar(0) < for i in a.indices { < total = total.addingProduct(a[i], b[i]) < } < return total < } --- > // func dot<V : SIMD>(_ a: V, _ b: V) -> V.Scalar where V.Scalar : FloatingPoint { > // var total = V.Scalar(0) > // for i in a.indices { > // total = total.addingProduct(a[i], b[i]) > // } > // return total > // } @_transparent public static func *(a: Self, b: Self) -> Self { var result = Self() for i in result.indices { result[i] = a[i] * b[i] } return result }public func SHOW_ME_MULPD(lhs: SIMD2<Double>, rhs: SIMD2<Double>) -> SIMD2<Double> { lhs * rhs } (edited)public func SHOW_ME_MULPD(lhs: SIMD2<Double>, rhs: SIMD2<Double>) -> SIMD2<Double> { lhs * rhs } (edited) .text .file "<swift-imported-modules>" .protected $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF .globl $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF .p2align 4, 0x90 .type $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF,@function $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF: mulpd %xmm1, %xmm0 retq .Lfunc_end0: .size $s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF, .Lfunc_end0-($s5CHECK13SHOW_ME_MULPD3lhs3rhss5SIMD2VySdGAG_AGtF) .hidden __swift_reflection_version .type __swift_reflection_version,@object .section .rodata,"a",@progbits .weak __swift_reflection_version .p2align 1 __swift_reflection_version: .short 3 .size __swift_reflection_version, 2 .type .L_swift1_autolink_entries,@object .section .swift1_autolink_entries,"e",@progbits .p2align 3 .L_swift1_autolink_entries: .asciz "-lswift_Concurrency\000-lswiftCore\000-lswift_StringProcessing" .size .L_swift1_autolink_entries, 57 .section ".linker-options","e",@llvm_linker_options .section ".note.GNU-stack","",@progbits (edited)mulpd %xmm1, %xmm0func dot<V : SIMD>(_ a: V, _ b: V) -> V.Scalar where V.Scalar : FloatingPoint { var total = V.Scalar(0) for i in a.indices { total = total.addingProduct(a[i], b[i]) } return total } このdotの実装はLLVMがうまくパターン見つけられなくて最適化されなかったのかもなぁ
1-O はレベル2なのでそもそも比較の土台が全然違った-O3 が必要じゃん。for i in 0 ..< 100 みたいなのでもアンロールされない?perf-statの結果 $ perf stat -d -d -- ./target.rust-run 5000000 Performance counter stats for './target.rust-run 5000000': 187.63 msec task-clock # 0.994 CPUs utilized 20 context-switches # 0.107 K/sec 0 cpu-migrations # 0.000 K/sec 85 page-faults # 0.453 K/sec 864088142 cycles # 4.605 GHz (32.07%) 385088 stalled-cycles-frontend # 0.04% frontend cycles idle (34.18%) 421948 stalled-cycles-backend # 0.05% backend cycles idle (36.31%) 1486120161 instructions # 1.72 insn per cycle # 0.00 stalled cycles per insn (38.44%) 18768109 branches # 100.027 M/sec (40.55%) 29184 branch-misses # 0.16% of all branches (42.36%) 543690464 L1-dcache-loads # 2897.670 M/sec (40.26%) 19859 L1-dcache-load-misses # 0.00% of all L1-dcache hits (38.12%) <not supported> LLC-loads <not supported> LLC-load-misses 141810 L1-icache-loads # 0.756 M/sec (35.99%) 2004 L1-icache-load-misses # 1.41% of all L1-icache hits (33.87%) 78 dTLB-loads # 0.416 K/sec (31.97%) 21 dTLB-load-misses # 26.92% of all dTLB cache hits (31.96%) 0 iTLB-loads # 0.000 K/sec (31.97%) 18 iTLB-load-misses # 0.00% of all iTLB cache hits (31.96%) 0.188802738 seconds time elapsed 0.188263000 seconds user 0.000000000 seconds sys$ perf stat -d -d -- ./head.swift-run 5000000 Performance counter stats for './head.swift-run 5000000': 260.12 msec task-clock # 0.996 CPUs utilized 26 context-switches # 0.100 K/sec 0 cpu-migrations # 0.000 K/sec 71 page-faults # 0.273 K/sec 1200493971 cycles # 4.615 GHz (30.86%) 451884 stalled-cycles-frontend # 0.04% frontend cycles idle (32.41%) 132 stalled-cycles-backend # 0.00% backend cycles idle (33.94%) 2904262641 instructions # 2.42 insn per cycle # 0.00 stalled cycles per insn (35.46%) 217096810 branches # 834.616 M/sec (37.00%) 10523 branch-misses # 0.00% of all branches (38.42%) 675633481 L1-dcache-loads # 2597.435 M/sec (38.42%) 10807 L1-dcache-load-misses # 0.00% of all L1-dcache hits (38.42%) <not supported> LLC-loads <not supported> LLC-load-misses 119631 L1-icache-loads # 0.460 M/sec (38.43%) 3361 L1-icache-load-misses # 2.81% of all L1-icache hits (38.41%) 6382 dTLB-loads # 0.025 M/sec (36.86%) 116 dTLB-load-misses # 1.82% of all dTLB cache hits (35.33%) 417 iTLB-loads # 0.002 M/sec (33.80%) 176 iTLB-load-misses # 42.21% of all iTLB cache hits (32.25%) 0.261201137 seconds time elapsed 0.260805000 seconds user 0.000000000 seconds syswithTaskGroup で解決したとツイートしてたのを見てから(探したけどツイートを発見できず)、 withGroupTask はちょっと大掛かりな気がして考えてたんだけど、一番シンプルな方法って↓かな? async let x = { print("foo") // 重めの同期処理 return 42 }() print("bar") print(await x) ↓の順で表示されるから、同期関数でもちゃんと child task として実行されている。 bar foo 42 前はよく let x = await Task.detached { print("foo") // 重めの同期処理 return 42 }.value print(x) としてたけど、これだと unstructured concurrency になってしまってキャンセル伝播しないから、 async let の方が良い気がする。 (edited)Task.yield() を呼び出すことでTask.yield が存在するのもこのような状況に対応するためという認識です。 (edited)Data.write とかは存在するわけで、メインスレッドで Data.write を待ちたくないし、別スレッドに逃したいケースはあるのでは?async に write する API を作るべきってこと?DispatchIO などの非同期IO を asyncでラップすることだと思います。async let x = { // (1) print("foo") // 重めの同期処理 return 42 }() ↑この書き方なんですけど、(1)が「メインスレッドに投入されない」保証はあるんですかね?async で細切れにして提供されるようになった上で、自前のループの中では適切に Task.yield を挟むというのをみんなが意識しないとですね。
withTaskGroup は動的なN個のためのAPIで、async let は定数個の場合のAPIなので、今回1件なのであれば withTaskGroup を async let に書き換える事自体は全く正しい改善だと思うのですが。async で細切れにして提供されるようになった上で、自前のループの中では適切に Task.yield を挟むというのをみんなが意識しないとですね。
async let x = { // (1) print("foo") // 重めの同期処理 return 42 }() ↑この書き方なんですけど、(1)が「メインスレッドに投入されない」保証はあるんですかね? import Foundation @MainActor func main() async { print(Thread.current.isMainThread) async let x = { print("foo", Thread.current.isMainThread) // 重めの同期処理 return 42 }() print("bar") print(await x) } await main()
true bar foo false 42 (edited)import Foundation @MainActor func main() async { print(Thread.current.isMainThread) async let x = { print("foo", Thread.current.isMainThread) // 重めの同期処理 return 42 }() print("bar") print(await x) } await main()
true bar foo false 42 (edited)
1DispatchQueue.global().async とかだとその保証はあったのかな?DispatchQueue.global().async とかだとその保証はあったのかな? async を呼び出して処理を投入した時、その前後順も無視されるんですよね 同時に走るDispatchQueue.global().async もメインスレッドで実行されるのでは?DispatchQueue.global().async もメインスレッドで実行されるのでは? DispatchQueue の場合と同じくらいにならないかなぁ。明確にドキュメントされてないのと、スレッド新しく作らない点が違い?DispatchQueue の場合と同じくらいにならないかなぁ。明確にドキュメントされてないのと、スレッド新しく作らない点が違い? sort とかでも n が増えたら詰まるよ?async 版の sort があったとしても、どっち使うべきか判断できない。詰まらないときは async 版使うの効率悪いし。MainActor context でない限りメインスレッドに投入されないのであれば、 iOS アプリでは重めの同期処理を投げても現実的にはあまり問題にはならなさそう。ThreadPool があって 後者のために EventLoop があって分けられているのでプログラマがコントロール可能。ThreadPool に明示的に逃せばいいっちゃいいが、swift concurrencyで完結できてないのはダサいよな。@globalActor で作れたと思うexecutor をオーバライドできるんじゃなかったかな。MainActor context でない限りメインスレッドに投入されないのであれば、 iOS アプリでは重めの同期処理を投げても現実的にはあまり問題にはならなさそう。 actor を使って ViewModel を書いたりするアーキテクチャでやってたりする場合ですかね。ObservableObject の objectWillChange がメインスレッド以外でトリガーされるのを禁止しているはずなので大丈夫そう。ObservableObject の objectWillChange がメインスレッド以外でトリガーされるのを禁止しているはずなので大丈夫そう。 @StateObject が必要になって、メインスレッド必須だから Main Actor にするしかなさそう。ViewModel.run1() はMainActorでは実行されないから、処理系が混んでたら止まる。 具体的にどうやってUIKitやSwiftUIに接続するか、とか、実際やってる人がいるかはわかんないです。ViewModel.run1() はMainActorでは実行されないから、処理系が混んでたら止まる。 具体的にどうやってUIKitやSwiftUIに接続するか、とか、実際やってる人がいるかはわかんないです。 actor を使うこともあるけど、最終的に View に接続するときに @MainActor な ObservableObject を挟むから大丈夫だと思う。直接は接続できないけど。async を避けるために Task.init にしたことで何が解決されてるのかはよくわからない。async にした方がいいと思う。 (edited)actor を使うこともあるけど、最終的に View に接続するときに @MainActor な ObservableObject を挟むから大丈夫だと思う。直接は接続できないけど。 @MainActor func updateUI(_ diffs: [Difference]) { /* ... */ } public extension SingleUpdate { func apply() { generateDiffs(from: rawContent, on: DispatchQueue.main) { diffs in unsafeAssumeOnMainActor { updateUI(diffs) } } } // ... }
https://forums.swift.org/t/pitch-unsafe-assume-on-mainactor/63074 実行時に本当にMainActorかのチェックもしてくれるみたいです。 The unsafeAssumeOnMainActor function is a nonisolated, non-async function that accepts a @MainActor closure. First, unsafeAssumeOnMainActor performs a runtime test to see if it has been called on the MainActor. If the assumption was wrong, the program will emit a diagnostic message and can either continue executing the closure or abort execution. (edited)HogeProtocol? は (any HogeProtocol)? になってくれるのに HogeProtocol! は any HogeProtocol! になっちゃって意味が変わるので結局手作業になっちゃうany 付与は完遂したのですが、以下の事象に出くわしました(メモ書き形式で雑ですが……) # Xcode 14.3 ## 正しい提案 - `HogeProtocol` → `any HogeProtocol` - `HogeProtocol?` → `(any HogeProtocol)?` - `as HogeProtocol` → `as (any HogeProtocol)`(`as?`・`as!` も同様) - `HogeProtocol.self` → `(any HogeProtocol).self` - `HogeProtocol.Type` → `any HogeProtocol.Type` ## ビルドできなくなる提案(100% 再現) - `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい ## ビルドできなくなる提案(ときどき再現) - `HogeProtocol.self` を `(any HogeProtocol).self` にしてくれないときがあった - `any HogeProtocol.self` にしたときがあった - Array でビルドできなくなるときがある - `let arr = [HogeProtocol]()` が `let arr = [any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しない - `let dict = [Key: HogeProtocol]()` が `let dict = [Key: any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しないany 付与は完遂したのですが、以下の事象に出くわしました(メモ書き形式で雑ですが……) # Xcode 14.3 ## 正しい提案 - `HogeProtocol` → `any HogeProtocol` - `HogeProtocol?` → `(any HogeProtocol)?` - `as HogeProtocol` → `as (any HogeProtocol)`(`as?`・`as!` も同様) - `HogeProtocol.self` → `(any HogeProtocol).self` - `HogeProtocol.Type` → `any HogeProtocol.Type` ## ビルドできなくなる提案(100% 再現) - `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい ## ビルドできなくなる提案(ときどき再現) - `HogeProtocol.self` を `(any HogeProtocol).self` にしてくれないときがあった - `any HogeProtocol.self` にしたときがあった - Array でビルドできなくなるときがある - `let arr = [HogeProtocol]()` が `let arr = [any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しない - `let dict = [Key: HogeProtocol]()` が `let dict = [Key: any HogeProtocol]()` に置換されたが、「file contains invalid or unrecognized Swift syntax.」と言われてビルドに失敗することがあった - 一度この状況になると clean したりしてもダメだった - 別なプロジェクト等では発生しない any を自動で付けるのってXcodeにリファクタリングメニュー(?)があるのでしょうか? それとも、コンパイルした後個別のエラーとして表示されて、FixItボタンを一個ずつ(?)押していますか?any 付けるのは機械的だからマイグレーターで自動でできるよって仕様書には書いてある。 https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md
(edited)any を自動で付けるのってXcodeにリファクタリングメニュー(?)があるのでしょうか? それとも、コンパイルした後個別のエラーとして表示されて、FixItボタンを一個ずつ(?)押していますか? c++ ERROR(existential_requires_any,none, "use of %select{protocol |}2%0 as a type must be written %1", (Type, Type, bool))
https://github.com/apple/swift/blob/main/include/swift/AST/DiagnosticsSema.def#L5159-L5161c++ std::string fix; llvm::raw_string_ostream OS(fix); if (needsParens) OS << "("; ExistentialTypeRepr existential(SourceLoc(), replaceRepr); existential.print(OS); if (needsParens) OS << ")"; if (auto *proto = dyn_cast_or_null<ProtocolDecl>(T->getBoundDecl())) { if (proto->existentialRequiresAny() && !Ctx.LangOpts.hasFeature(Feature::ImplicitSome)) { Ctx.Diags.diagnose(T->getNameLoc(), diag::existential_requires_any, proto->getDeclaredInterfaceType(), proto->getDeclaredExistentialType(), /*isAlias=*/false) .fixItReplace(replaceRepr->getSourceRange(), fix); } }
https://github.com/apple/swift/blob/1b6d160c5ce9b55cf82bd4ee0df51774d0410e03/lib/Sema/TypeCheckType.cpp#L5201 (edited)c++ auto parameterized = ParameterizedProtocolType::get(ctx, protoType, argTys); diags.diagnose(loc, diag::existential_requires_any, parameterized, ExistentialType::get(parameterized), /*isAlias=*/isa<TypeAliasType>(type.getPointer()));
https://github.com/apple/swift/blob/1b6d160c5ce9b55cf82bd4ee0df51774d0410e03/lib/Sema/TypeCheckType.cpp#L778-L785c++ needsParens = existentialNeedsParens(*parentIt);c++ bool existentialNeedsParens(TypeRepr *parent) { switch (parent->getKind()) { case TypeReprKind::Optional: case TypeReprKind::Protocol: return true; case TypeReprKind::Metatype: case TypeReprKind::Attributed: case TypeReprKind::Error: case TypeReprKind::Function: case TypeReprKind::Ownership: case TypeReprKind::Composition: case TypeReprKind::OpaqueReturn: case TypeReprKind::NamedOpaqueReturn: case TypeReprKind::Existential: case TypeReprKind::SimpleIdent: case TypeReprKind::GenericIdent: case TypeReprKind::Member: case TypeReprKind::Dictionary: case TypeReprKind::ImplicitlyUnwrappedOptional: case TypeReprKind::Tuple: case TypeReprKind::Fixed: case TypeReprKind::Array: case TypeReprKind::SILBox: case TypeReprKind::Isolated: case TypeReprKind::Placeholder: case TypeReprKind::CompileTimeConst: case TypeReprKind::Vararg: case TypeReprKind::Pack: case TypeReprKind::PackExpansion: case TypeReprKind::PackElement: return false; } }case TypeReprKind::ImplicitlyUnwrappedOptional: を上にもってくるだけでなんかうまくいったりしねえかなw (edited)case TypeReprKind::Composition: のパターンもあるけどこれはそう簡単な話かどうか微妙だなfile contains invalid unrecognized Swift syntax は swift-format のメッセージ(だった)っぽいです[any HogeProtocol]() をパースできなかった、とかですかねany P? を (any P)? にする対応はここの分岐で書かれているので,ここに any P! を (any P)! にする対応を追加する必要があるのかもしれないですね. https://github.com/apple/swift/blob/c7766763e8ef7b0425c95c8c688f34fcaa884f26/lib/Sema/TypeCheckType.cpp#L4845-L4857- `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい について 2つ issue を書いてみようと思います。any P? を (any P)? にする対応はここの分岐で書かれているので,ここに any P! を (any P)! にする対応を追加する必要があるのかもしれないですね. https://github.com/apple/swift/blob/c7766763e8ef7b0425c95c8c688f34fcaa884f26/lib/Sema/TypeCheckType.cpp#L4845-L4857 P? を (any P)? にしてるこちらを改良して P! を (any P)! にする機能も追加したほうが一手短くて良いと思います https://discord.com/channels/291054398077927425/306995750418513920/1094085905586855937 (edited)
1- `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい について 2つ issue を書いてみようと思います。 ExistentialAny · Issue #65026 · apple/swift https://github.com/apple/swift/issues/65026- `HogeProtocol!` を `any HogeProtocol!` にしてしまう - `HogeProtocol?` は `(any HogeProtocol)?` になってくれるのに…… - `HogeProtocol & FugaProtocol` を `any HogeProtocol & any FugaProtocol` にしてしまう - `any HogeProtocol & FugaProtocol` にしてほしい について 2つ issue を書いてみようと思います。 & で繋ぐことを "Protocol Composition Type" というんですね… 知りませんでした) The suggested fix for the protocol composition type is incorrect when enable upcoming featureExistentialAny · Issue #65027 · apple/swift https://github.com/apple/swift/issues/65027
1ObservedChanges emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run.
The ObservedValues asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension. changesのグルーピングのタイミングがアイソレーションの中断ごとというところがまだイメージできてないんですよね...public struct ObservedChange<Subject: Observable>: @unchecked Sendable { public func contains(_ member: PartialKeyPath<Subject>) -> Bool } extension ObservedChange where Subject: Sendable { public var subject: Subject { get } } /// An asynchronous sequence of observed changes. public struct ObservedChanges<Subject: Observable, Delivery: Actor>: AsyncSequence { public typealias Element = ObservedChange<Subject> public struct Iterator: AsyncIteratorProtocol { public mutating func next() async -> Element? } public func makeAsyncIterator() -> Iterator } extension ObservedChanges: @unchecked Sendable where Subject: Sendable { } @available(*, unavailable) extension ObservedChanges.Iterator: Sendable { }subject から取り出せて、「何が変わったか」が contains(PartialKeyPath) で調べられる?ObservedChanges emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run.
The ObservedValues asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension. changesのグルーピングのタイミングがアイソレーションの中断ごとというところがまだイメージできてないんですよね... ObservedChanges emits values when a change is made and an available suspension point has been met by the isolation. This means that changes can be grouped up transactionally to the boundary of when a given actor is available to run.
The ObservedValues asynchronous sequence on the other hand will await a change to values end emit the latest value that is available between the last iteration (or starting point of iteration) and the suspension. changesのグルーピングのタイミングがアイソレーションの中断ごとというところがまだイメージできてないんですよね... access とか withMutation のスコープ関数で囲って実現してるような気がしたけど、 これらはプロパティの内側で呼ばれるから無理そうなんですよね どうやってこの「ひとまとめにするグループ」を制御しているんだろう。nextChangeが呼ばれていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservedChanges.swift#L93 その中でinsertNextChangeが呼ばれて、さらにその中で各Stateによって処理が分かれるのですが、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L312 Pending中は値をinsertしていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrarStateMachine.swift#L54 来たるべきタイミングの時にcontinuation.resumeで変更した値を全部emitしているのではないか思っています。 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L323@StateObject や @ObservedObject なくなると Binding が簡単に取れなくなって困らないのかなぁ。 @Observable final class Model { var order: Order? var account: Account? var hasAccount: Bool { return userCredential != nil && account != nil } var favoriteSmoothieIDs: Set<Smoothie.ID> = [] var selectedSmoothieID: Smoothie.ID? var searchString: String = "" var isApplePayEnabled: Bool = true var allRecipesUnlocked: Bool = false var unlockAllRecipesProduct: Product? } struct SmoothieList: View { var smoothies: [Smoothie] var model: Model var listedSmoothies: [Smoothie] { smoothies .filter { $0.matches(model.searchString) } .sorted(by: { $0.title.localizedCompare($1.title) == .orderedAscending }) } var body: some View { List(listedSmoothies) { smoothie in ... } } }@Observable マクロががんばってくれたりはしない? extension MyObject { var someComputedProperty: Int { somePrivateProperty + someOtherProperty } nonisolated static func dependencies( of keyPath: PartialKeyPath<Self> ) -> TrackedProperties<Self> { switch keyPath { case \.someComputedProperty: return [\.somePrivateProperty, \.someOtherProperty] default: return [keyPath] } } }nextChangeが呼ばれていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservedChanges.swift#L93 その中でinsertNextChangeが呼ばれて、さらにその中で各Stateによって処理が分かれるのですが、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L312 Pending中は値をinsertしていて、 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrarStateMachine.swift#L54 来たるべきタイミングの時にcontinuation.resumeで変更した値を全部emitしているのではないか思っています。 https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L323 @Observable マクロががんばってくれたりはしない? extension MyObject { var someComputedProperty: Int { somePrivateProperty + someOtherProperty } nonisolated static func dependencies( of keyPath: PartialKeyPath<Self> ) -> TrackedProperties<Self> { switch keyPath { case \.someComputedProperty: return [\.somePrivateProperty, \.someOtherProperty] default: return [keyPath] } } } foo.a = 3 ) その単位で didSet が起きて発行して終わるような気がする・・・dependencies は、全てのプロパティに依存してる設定でマクロ生成されるようですよremoveDuplicates しておけばマシになるかな。dependencies は、全てのプロパティに依存してる設定でマクロ生成されるようですよ dependencies 独自実装しようとすると、それ以外のプロパティも実装しないといけないことになって、自動実装が消えてミス多発しそう・・・。insertNextChange が idle中に来るとactiveになる pending中に来ると、そこまで集めた変更(があれば)をresume送信してidleになるnext() が呼ばれるからnext() が呼ばれるまでの間」に、発生したdidSetがpending stateとして累積されているんだわ (edited)@MainActor func handleChanges(_ example: ChangesExample) { Task { @MainActor in for await change in example.changes(for: [\.someField, \.someOtherField]) { switch (change.contains(\.someField), change.contains(\.someOtherField)) case (true, true): print("Changed both properties") case (true, false): print("changed integer field") case (false, true): print("changed string field") default: fatalError("this case will never happen") } } } }example.changes().next が for-await によって呼ばれて・・・.next の結果を送信するための Continuationが、MainActorコンテキストなTaskから呼びされた場合はwithCheckedContinuation系のみなはずなんですが、例外が増えるということなんでしょうかねwithCheckedContinuation系のみなはずなんですが、例外が増えるということなんでしょうかね withUnsafeContinuation なんですよ。 送信してるところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L250 Continuationを作ったところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L432 (edited)withUnsafeContinuation なんですよ。 送信してるところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L250 Continuationを作ったところ: https://github.com/apple/swift/blob/main/stdlib/public/Observation/Sources/Observation/ObservationRegistrar.swift#L432 (edited)scheduleNextChange自体がasync関数なので1フレ遅れ得ませんかね?ObservedChanges.next → ObservationRegistrar.nextChange (isolated) → ObservationRegistrar.scheduleNextChange (isolated) → withUnsafeContinuation ObservedChanges は init で isolation: Isolation を受け取る。func viewDidLoad(…) { … handleChanges(example) example.someField = 10 // ここ } の「ここ」の変更は通知されないと思っているのですが、合ってますでしょうか?
func viewDidLoad(…) { … handleChanges(example) example.someField = 10 // ここ } の「ここ」の変更は通知されないと思っているのですが、合ってますでしょうか?
Task { for await value in chain([object.someProperty].async, object.values(for: \.someProperty)) { // Really important processing of the value } }
https://forums.swift.org/t/se-0395-observability/64342/24 (edited)if a property will be observed and the change in that property only happens once (and never happens again) and the confluence of events is such that the property observation via values(for:) is called asynchronously (e.g. in a Task) the first value could get lost. ただし、 初期値の取得はイテレーションと同じ分離領域で取得する必要がある 初期値が全部のケースで必要というわけではない 初期値を保持することでどこかのライフサイクルに影響を与えるかもしれない といったことを考慮しなければならない、と書かれてました。 https://forums.swift.org/t/se-0395-observability/64342/89 /// Returns a URL constructed by appending the given path to self. /// - Parameters: /// - path: The path to add /// - directoryHint: A hint to whether this URL will point to a directory @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) public func appending<S>(path: S, directoryHint: URL.DirectoryHint = .inferFromPath) -> URL where S : StringProtocol
https://forums.swift.org/t/back-from-revision-foundation-url-improvements/54605/28URL.appending() 、 Linux版 Foundation に全然追加されないのなんなんだろ? ソース(なさそう): https://github.com/apple/swift-corelibs-foundation/blob/2d23cf3dc07951ed2b988608d08d7a54cc53b26e/Sources/Foundation/URL.swift#L199 実行結果(なさそう): https://discord.com/channels/291054398077927425/430242233468452865/1101699773775499265 (edited)URL に限らず Foundation の API 変更への追従は基本遅いですよね.Date.now も iOS 15 で使えるようになって swift-corelibs-foundation にも乗って Swift のリリースに乗ったのが Swift 5.8 なので,1 年半遅れて Linux で使えるようになりましたし. https://github.com/apple/swift-corelibs-foundation/pull/4609 (edited)URL に限らず Foundation の API 変更への追従は基本遅いですよね.Date.now も iOS 15 で使えるようになって swift-corelibs-foundation にも乗って Swift のリリースに乗ったのが Swift 5.8 なので,1 年半遅れて Linux で使えるようになりましたし. https://github.com/apple/swift-corelibs-foundation/pull/4609 (edited)Date.now の事、知りませんでした。 こんな簡単なやつも実装されないのか。 dannflorさんはAppleの人ではないみたいだし、野良のコントリビューションで対応されたのか・・・ あまりにもやる気ないな・・・
1withObservationTrackingを使ってsyncのfor loopでtrackingできるようになったり、個々のプロパティをTrackingから除外する@ObservationIgnoredが追加になったり、色々変更になるようです。 ObservationTrackingという型に@_spi(SwiftUI)がついているのがよくわかってないです@_spiはモジュールプライベートみたいなアクセス制御で普通にImportしても見えなくてImportしたい場合はImportする側で@_spi(SwiftUI) importって書くと見えるやつです。
1@_spi(SwiftUI)ついてると明確にSwiftUI意識されてることわかって面白いですね@_spi つけてimportされた型って再exportされるんでしたっけ?それとも@_implementationOnly相当?/tmp/tmp.7NtbKdeUsR/Sources/Bar/Bar.swift:3:29: error: cannot use struct 'BarPrivate' here; it is an SPI imported from 'Foo' public func reExport(_: Foo.BarPrivate) { ^ Foo.BarPrivate:1:15: note: type declared here public struct BarPrivate { ^ /tmp/tmp.7NtbKdeUsR/Sources/Bar/Bar.swift:3:29: error: cannot use struct 'BarPrivate' here; it is an SPI imported from 'Foo' public func reExport(_: Foo.BarPrivate) { ^ Foo.BarPrivate:1:15: note: type declared here public struct BarPrivate { ^@_spi(ForBar) を再エクスポートしてる宣言にもつけるとできる (edited)ObservationTrackingは完全にユーザが意識しない型なんですね.target(name: "MyTarget", dependencies:[.fancyLibrary], swiftSettings: [.enableUpcomingFeature(name: “ConciseMagicFile”), .enableUpcomingFeature(name: “BareSlashRegexLiterals”), .enableUpcomingFeature(name: “ExistentialAny”)]) (edited)struct UpcomingFeatureName: ExpressibleByStringLiteral { static let conciseMagicFile = UpcomingFeatureName("ConciseMagicFile") (edited)-warn-concurrency 以降の変更が反映されていないですが確かにそれが一番近そうですね.Rust の https://doc.rust-lang.org/unstable-book/ みたいに別ドキュメントが必要になっちゃうのも管理が面倒そうなので Swift Evolution だけで済むようになっていけば確かにその方が良いなとは思います.-strict-concurrency は書いてないですよね。Array や Dictionary に入れることを考えると、これまで要素の一つが変更されても全体の変更として検出されてしまっていたのが、 @Observable なクラスを使えば要素単体の変更として検出できて効率的。 これまでも全部 ObservableObject にして Array や Dictionary の要素の変更を直接検出する作戦はあったかもしれないけど、全部 ObservableObject にしちゃうと ObservableObject が ObservableObject を保持してるときに、前者(保持してる側)を @StateObject 等で保持しても後者(保持されてる側)の変更を自動的に検出できるわけでなかったのであまり現実的でなかったけど、 Observation を使えば @StateObject とかなくても後者の変更を直接検出できる。@Observable struct は作れなくなったようです。 https://discord.com/channels/291054398077927425/499393715140558881/1132302591146078299 あと、今見つけられないのですが、前に shiz さんが値型の @Observable には問題があるという意見をコアチームの誰かが発言してるフォーラムの投稿を挙げてくれてた気が・・・。Timeline が var posts: [Post] を持つようなケースで、タイムラインのページから投稿の一つをタップして投稿詳細ページに遷移、遷移先で post.isLiked.toggle() したら遷移元のタイムラインにも反映させたいようなケースで、これまでは Post を struct にして @Binding で渡せばよかったですが、 Observation の恩恵を受けるには Post を @Observable class にしないといけないですよね。@Binding で 渡すのではなく、遷移元と遷移先で共有する ObservableObject が Timeline を保持していて、その ObservableObject を参照するとかかもですが、いずれにせよ本題は Observation の恩恵を受けるために Post が @Observable class でないといけなくならないかという点で、その点には影響ないかと思います。 (edited)Binding だと、 post.isLiked,toggle() でタイムラインの View の body も走っちゃいませんか? (edited)posts 自体は変更されてるから( 1 要素の isLiked だけとはいえ)、タイムライン側で == しても変更があるとしか言えなくないですか?View のレンダリングは行われなくても body は実行されないですか?posts 自体が変わっても各要素で差分ある値のみレンダリングされる認識です。View の body は勝手に再実行されませんでしたっけ?これは気のせいかも。 (edited)_printChanges() でひたすらログを眺めてたらそんな感じだったようなArray や Dictionary でエンティティを保持しており、その 1 要素の 1 プロパティが変更されただけで、それを参照しているすべての body の再実行がされ、その中にはループして O(N) のものもあり、実 View のレンダリングは行われないにしても、仮想 View の再生成すら辛い状況が、特にハイパフォーマンスが求められるアプリでは起こっているのかなと。State 型を作るという話ではなく、 struct を Array や Dictionary に入れてしまった時点で、そのコレクション全体の変更として検出されてしまうという問題についてですね(SSOTの話を出したのは、そのコレクションが色々なところから参照された場合に影響度が大きいという話です)。 これまでも各要素を ObservableObject で表現すれば要素の変更を検出できましたが、ネストした ObservableObject の変更を(自動的には)検出できなかったのでいまいちでした。 Observation によって変更箇所がピンポイントで検出できるようになったけれども、それに乗っかるには @Observable class にしないといけない( struct にできない)というのが悩んでいるポイントです。 (edited)T のコピーをしている可能性がある。@main struct MyApp: App { var body: some Scene { WindowGroup { Text("Hello, world!") } } }SwiftUI.App に渡されるところから始まるので。
1
1var a: Foo = .init() var b: Foo = a withObservationTraking { print(a.x) } onChange: { print("onChange") } b.x = 42 で onChange が呼ばれるのか。呼ばれるなら、異なるインスタンスである a と b の observation が混ざってて変だし(このあと、 a.x = -1 を実行するとどうなるのか)、呼ばれないならコピーされたら(たとえば computed property を介して Foo が return されたら)観測できなくなる。 (edited)ObservableObject とほぼ同じ使い方になる。
1struct S<X> { var x: X } extension S: Equatable where X: Equatable {} extension S: Hashable where X: Hashable {}struct S<X> { var x: X } extension S: Equatable where X: Equatable {} extension S: Hashable where X: Hashable {} struct S<X> { var x: X } extension S: Hashable where X: Equatable { var hashValue: Int { 1 } } extension S: Equatable where X: Hashable {}struct S<X> { var x: X } extension S: Hashable where X: Equatable { var hashValue: Int { 1 } } extension S: Equatable where X: Hashable {} <stdin>:2:1: error: type 'X' does not conform to protocol 'Hashable' extension S: Hashable where X: Equatable { ^ <stdin>:2:1: error: 'Hashable' requires that 'X' conform to 'Hashable' extension S: Hashable where X: Equatable { ^ <stdin>:2:1: note: requirement specified as 'X' : 'Hashable' extension S: Hashable where X: Equatable { ^ <stdin>:2:1: note: requirement from conditional conformance of 'S<X>' to 'Equatable' extension S: Hashable where X: Equatable { ^ <stdin>:3:7: warning: 'Hashable.hashValue' is deprecated as a protocol requirement; conform type 'S' to 'Hashable' by implementing 'hash(into:)' instead var hashValue: Int { 1 } ^struct S<X> { var x: X } extension S: Hashable where X: Equatable { var hashValue: Int { 1 } } extension S: Equatable where X: Hashable {} S: Hashable の条件は、 S: Equatable の条件を満たしていないといけない、って制約がかかってる!賢い。protocol ExpressibleByStringLiteral : ExpressibleByExtendedGraphemeClusterLiteral {} protocol ExpressibleByExtendedGraphemeClusterLiteral : ExpressibleByUnicodeScalarLiteral {} すごい、孫プロトコルだ 知らなかったstruct S<X> {} extension S: Error where X: Error {}struct S<X> {} extension S: Error where X: Error {} S: Error は S: Sendable も付いてきてるな・・・S が Sendable になる条件が複数あっても大丈夫・・・?X: Sendable を2つ書いたら怒られた。extension S: Error where X: Error {} って書くと自動的に extension S: Sendable where X: Error {} も生成されている(条件部分はコピー) ような挙動に見えますね。。 (edited)extension S: Sendable where X: Sendable {} を書けば、自然な定義にはなるから、困ることは無さそうだけど、ルールが違うのが気になるな: Error がソース互換性を壊すから、仕方なく特別処理してるのか・・・? (edited)Error)に対してcond confの条件にできない、だから、逆にいえば必須にもできないのかなとextension S: Error where X: Sendableが記載できないから、エラーにできないのかなとextension S: Error where X: Error から、 暗黙に extension S: Sendable where X: Error が生成されているよね、というのはextension S: Error where X: Sendable ←禁止されているのはこっちですね これは実行時に X: Sendable がテストできないから駄目(情報自体が実行時には消えている) (edited)extension S: Sendable where X: Error ←こっちは実行時に条件部をテストする事自体はできる。 できるけど、 Sendableへの動的キャストが禁止されているから意味が無い。extension S: Error where X: Error に対して extension S: Sendable where X: Error {} が自動で定義されるのはちょっと不思議ですねS: Hashable の場合と同様に、 S: Sendable も明記しなさい、で良いと思うんですけど・・・Error に対して親プロトコルの Sendable が後付けされたので、 既存の S: Error where がコンパイル不可能になるのを避けたのかなあ? (edited)c++ if (conformance->getSourceKind() == ConformanceEntryKind::Implied && !Proto->isMarkerProtocol()) {ERROR(conditional_conformances_cannot_imply_conformances,none, "conditional conformance of type %0 to protocol %1 does not imply conformance to " "inherited protocol %2", (Type, Type, Type)) のエラーが出るようになってる。Error), and is safe because there's no runtime component to marker protocols. func transaction<T>(_ closure: @Sendable @escaping (Database) async throws -> T) async throws -> T { try await self.transaction { db -> EventLoopFuture<T> in let promise = self.eventLoop.makePromise(of: T.self) promise.completeWithTask{ try await closure(db) } return promise.futureResult }.get() } (edited)transaction 関数の引数の closure って、 @Sendable も @escaping も外していいと思うんですけど、正しい考えですかね?self.transaction が返す EventLoopFuture を await future.get() して待機しているから、 この transaction メソッドが終了する時点では、必ず closure の呼び出しも終了しているからです。closure はEventLoopスレッドで実行されるのでconcurrency boundaryは跨いでるけど、 この transaction メソッド自体が async で、呼び出し側は必ず await で待たないといけないし。let closure = { (db) in ... } Task { closure() } try await db.transaction(closure) ↑呼び出し側で勝手にこういうふうにする事ができるか・・・? でもこの場合は、let closure の時点で @Sendable にしないと Task に渡せないから、この心配もないか@escapingはなくても良さそうですね。@Sendableに関してもclosureは一瞬EventLoopと別Taskのスレッドに移るけど、reentrantを考えてもclosureと同じActor contextの処理が並列実行されることはないような気がする?ので安全なようなtransaction function に対して no って言ってるな・・・class NotSendableString { /* ... */ } final class Address: Sendable { /* ... */ } func greetCharlie(_ charlie: Charlie) {} actor Charlie { var score: Int let fixedNonSendable: NotSendableString let fixedSendable: Address var me: Charlie? = nil func incrementScore() { self.score += 1 } nonisolated func nonisolatedMethod() {} init(_ initialScore: Int) { self.score = initialScore // ✅ self.fixedNonSendable = NotSendableString("Charlie") // ✅ self.fixedSendable = NotSendableString("123 Main St.") // ✅ if score > 50 { nonisolatedMethod() // ✅ selfのnonisolatedな使用 greetCharlie(self) // ✅ selfのnonisolatedな使用 self.me = self // ✅ selfのnonisolatedな使用 } else if score < 50 { score = 50 } assert(score >= 50) // ❌ selfのnonisolatedな使用後に、可変のisolatedなプロパティへアクセスできなくなる _ = self.fixedNonSendable // ❌ selfのnonisolatedな使用後に、non-Sendableなプロパティへアクセスできなくなる _ = self.fixedSendable Task { await self.incrementScore() } // ✅ selfのnonisolatedな使用は引き続きOK(awaitしているのでactor isolated) } } (edited)nonisolated は isolated の間違いですかね? (edited)Task { await self.incrementScore() } // ✅ selfのnonisolatedな使用は引き続きOK そうですね。これはawaitしてactorのboudary内の処理になるのでコメントが変だと思いました。 (edited)nonisolated selfとして扱うようにすることをdecayと呼んでいます。 the isolation of self decays (or changes) to nonisolated during any use of self that is not a direct stored-property access
(edited)nonisolated self になっているってことなのか、なるほど。var a0 = [0] a0 += [a] a0 += [b] let a = consume a0
1Task { await self.incrementScore() } // ✅ selfのnonisolatedな使用は引き続きOK そうですね。これはawaitしてactorのboudary内の処理になるのでコメントが変だと思いました。 (edited)Task.init に self を capture させるところが nonisolated use of self だと言っているのかもしれません。 nonisolatedなので actor context の引継ぎもおきなくて、 中で await が必要ですTask.init に self を capture させるところが nonisolated use of self だと言っているのかもしれません。 nonisolatedなので actor context の引継ぎもおきなくて、 中で await が必要です
1throws CatError ↓ throws(CatError) rethrowsにも型を指定できるようになった func foo<E: Error>(closure: () throws(E) -> Void) rethrows(E) Resultはtyped errorを返すように変える extension Result { init(catching body: throws(Failure) -> Success) { ... } } 当時よりも型推論がパワーアップしてもっと簡単に書けるようになった。 enum CatError: Error { case sleeps case sitsAtATree } do { try callCat() // throws CatError if something { throw CatError.asleep // throws CatError } } catch .asleep { openFoodCan() } // note: CatError can be thrown out of this do...catch block when the cat isn't asleep**Rationale**: #if FOUNDATION_FRAMEWORK @_implementationOnly import _ForSwiftFoundation /// Wraps an `NSCalendar` with more Swift-like `Calendar` API. See also: `_NSSwiftCalendar`. /// This is only used in the case where we have custom Objective-C subclasses of `NSCalendar`. It is assumed that the subclass is Sendable. internal final class _CalendarBridged: _CalendarProtocol, @unchecked Sendable { let _calendar: NSCalendar#if があって、#if FOUNDATION_FRAMEWORK @_implementationOnly import _ForSwiftFoundation import CoreFoundation #else #endif こういうのもあるからFoundation.framework がインポートできる環境において機能していて、// MARK: - Bridging #if FOUNDATION_FRAMEWORK @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Calendar : ReferenceConvertible { public typealias ReferenceType = NSCalendar } @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension Calendar : _ObjectiveCBridgeable { @_semantics("convertToObjectiveC") public func _bridgeToObjectiveC() -> NSCalendar { _calendar.bridgeToNSCalendar() } public static func _forceBridgeFromObjectiveC(_ input: NSCalendar, result: inout Calendar?) { if !_conditionallyBridgeFromObjectiveC(input, result: &result) { fatalError("Unable to bridge \(_ObjectiveCType.self) to \(self)") } } public static func _conditionallyBridgeFromObjectiveC(_ input: NSCalendar, result: inout Calendar?) -> Bool { result = Calendar(reference: input) return true } @_effects(readonly) public static func _unconditionallyBridgeFromObjectiveC(_ source: NSCalendar?) -> Calendar { var result: Calendar? _forceBridgeFromObjectiveC(source!, result: &result) return result! } } @available(macOS 10.10, iOS 8.0, watchOS 2.0, tvOS 9.0, *) extension NSCalendar : _HasCustomAnyHashableRepresentation { // Must be @nonobjc to avoid infinite recursion during bridging. @nonobjc public func _toCustomAnyHashable() -> AnyHashable? { return AnyHashable(self as Calendar) } } #endif // FOUNDATION_FRAMEWORK@_implementationOnly import _ForSwiftFoundationFoundation.framework の中に _ForSwiftFoundation というモジュールがあるはずで、 // _CalendarICU, if present static var calendarICUClass: _CalendarProtocol.Type = { #if FOUNDATION_FRAMEWORK _CalendarICU.self #else if let name = _typeByName("FoundationInternationalization._CalendarICU"), let t = name as? _CalendarProtocol.Type { return t } else { // Use the default gregorian class return _CalendarGregorian.self } #endif }()_CalendarICU って型も corelibs-foundation の中から見つけられない。謎が深まる。SendNonSendable で、トゲナシトゲトゲみたいでわかりづらいが#isolation、 @inheritsActorIsolation のアンスコ卒業
1$ file ./usr/bin/swift ./usr/bin/swift: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit x86_64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|WEAK_DEFINES|BINDS_TO_WEAK|PIE|HAS_TLV_DESCRI PTORS>] [arm64]
1do throws(CatError) { if isDaylight && foodBowl.isEmpty { throw .sleeps // equivalent to CatError.sleeps } try callCat() } catch let myError { // myError is of type CatError } (edited)Task(on: executor) { // starts and runs on the 'executor' await nonisolatedAsyncFunc() } Task.detached(on: executor) { // starts and runs on the 'executor' await nonisolatedAsyncFunc() } await withDiscardingTaskGroup { group in group.addTask(on: executor) { // starts and runs on the 'executor' await nonisolatedAsyncFunc() } } func nonisolatedAsyncFunc() async -> Int { // if the Task has a specific executor preference, // runs on that 'executor' rather than on the default global concurrent executor return 42 }
https://forums.swift.org/t/se-0417-task-executor-preference/68958 (edited)
2FullTypedThrows なので正式にはSwift6からかもしれないですね。 今のところ、experimental feature flagに TypedThrows付けるとmainブランチでは動くみたいです。release/5.10には入れてはいなさそうに見えますね。
(edited)Limitations of preamble and body macros: While the proposed design for preamble macros makes it possible to inject both prologue and epilogue code using defer, the inability to examine a thrown error or the function's return value were cited as difficult limitations.
Lack of closure support: While the Language Steering Group does agree that it would be useful to have this capability, applying function body macros to closures presents a number of unique challenges that do not apply to regular function declarations. For this reason, we are comfortable slicing closures off as a future direction for their own proposal.
https://forums.swift.org/t/returned-for-revision-se-0415-function-body-macros/69114
(edited)Limitations of preamble and body macros: While the proposed design for preamble macros makes it possible to inject both prologue and epilogue code using defer, the inability to examine a thrown error or the function's return value were cited as difficult limitations.
Lack of closure support: While the Language Steering Group does agree that it would be useful to have this capability, applying function body macros to closures presents a number of unique challenges that do not apply to regular function declarations. For this reason, we are comfortable slicing closures off as a future direction for their own proposal.
https://forums.swift.org/t/returned-for-revision-se-0415-function-body-macros/69114
(edited)@OptionSetがstdlibに追加されて、その後しれっと削除された様なのですが、SwiftMacro内には存在し続けて配布が続いている様です。これってどういう扱いになってるのでしょう? @swift-5.9.2 @swift-5.10.1 -Xfrontend -print-ast-decl @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") @OptionSet<UInt8> struct ShippingOptions { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } struct ShippingOptionsWithNoMacro { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] }@OptionSetがstdlibに追加されて、その後しれっと削除された様なのですが、SwiftMacro内には存在し続けて配布が続いている様です。これってどういう扱いになってるのでしょう? @swift-5.9.2 @swift-5.10.1 -Xfrontend -print-ast-decl @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") @OptionSet<UInt8> struct ShippingOptions { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } struct ShippingOptionsWithNoMacro { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") internal struct ShippingOptions {@OptionSetがstdlibに追加されて、その後しれっと削除された様なのですが、SwiftMacro内には存在し続けて配布が続いている様です。これってどういう扱いになってるのでしょう? @swift-5.9.2 @swift-5.10.1 -Xfrontend -print-ast-decl @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") @OptionSet<UInt8> struct ShippingOptions { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } struct ShippingOptionsWithNoMacro { private enum Options: Int { case nextDay case secondDay case priority case standard } static let express: ShippingOptions = [.nextDay, .secondDay] static let all: ShippingOptions = [.express, .priority, .standard] } @attached(member, names: named(RawValue), named(rawValue), named(`init`), arbitrary) @attached(extension, conformances: OptionSet) public macro OptionSet<RawType>() = #externalMacro(module: "SwiftMacros", type: "OptionSetMacro") internal struct ShippingOptions {If the feature is merged but doesn’t seem to be making progress, it may be removed. For what it’s worth, that has happened to work by Apple engineers.
https://forums.swift.org/t/pitch-optionset-macro/63547/167 Embedded buildには残っているですかね? https://github.com/apple/swift/pull/69197 (edited)If the feature is merged but doesn’t seem to be making progress, it may be removed. For what it’s worth, that has happened to work by Apple engineers.
https://forums.swift.org/t/pitch-optionset-macro/63547/167 Embedded buildには残っているですかね? https://github.com/apple/swift/pull/69197 (edited)SwiftMacroライブラリに実装は残っててstdlibへの登録が削除されてるみたいですね。
(edited)@OptionSet少し使ってみましたが、enum OptionsのメンバーにつけたDoc commentが生成されたメンバに引き継がれないので、使わない方が便利という結論になりました。Fermyon ってフェルミ粒子のことか spinは素粒子のスピンに掛けてるのか
1public extension Database { func transaction<T>(_ closure: @Sendable @escaping (Database) async throws -> T) async throws -> T { try await self.transaction { db -> EventLoopFuture<T> in let promise = self.eventLoop.makePromise(of: T.self) promise.completeWithTask{ try await closure(db) } return promise.futureResult }.get() } この closure から @Sendable は外せない。 理由は、 closure が EventLoopFuture の中で実行されている間に、 この executor は await しているだけでブロックはされていないから。 例えば closure が Actor の中にあった場合、 Actor のメソッド自体は interleave するから、同じisolationからこの closure を呼び出される可能性があって、 EventLoop threadとの並行実行になりうる。transferring を使えば closure が呼び出し元でコピーが生き残ってない事を要請できて @Sendable が外せるんだろうか?When a call passes an argument to a transferring parameter, the caller cannot use the argument value again after the callee returns.
https://github.com/apple/swift-evolution/blob/main/proposals/0430-transferring-parameters-and-results.md#ownership-convention-for-transferring-parameters (edited)
1XCTestCase.setup とかが同期を要求してて困ったりするんだよなXCTestCase.setUp は async throws 版が使えませんか? https://developer.apple.com/documentation/xctest/xctest/3856481-setupXCTestCase.setUp は async throws 版が使えませんか? https://developer.apple.com/documentation/xctest/xctest/3856481-setup
1Expansion of Swift Macros in Visual Studio Code by @lokeshtr and mentor @ahoppen and @adam-fowler Swift-etcd: A Native Client for Seamless Integration with etcd Servers by @ayushi2103 and mentor: @FranzBusch Lexical scopes for swift-syntax by @MAJKFL and mentor: @Douglas_Gregor Add a deploy SwiftPM plugin and a Swift-based DSL to the Swift runtime for AWS Lambda by @esraaeiid and mentor: @sebsto (edited)@alwaysEmitIntoClient だったら遅延してるのか!!$ swift package foo が増えすぎてるから、 $ swift package manifesto foo みたいなサブコマンド空間に整理するのはどうですか、みたいなスレadd から始まるから(記憶がおぼろげだと)補完を引き出すのが難しくないですか? (edited)manifesto とか target だったら、 全件補完で見た時に探してるものだと推測しやすいし、 man まで打ってから、補完して、その次はマニフェスト編集に使えるサブコマンドが一覧されるから便利。swift-packageへのシンボリックリンクになってるサブコマンド群のほとんどにshell completionが効かないですよね。swift-argument-parser を使ってるから、補完定義ファイルも生成できるんじゃあないんかなarray[1] とか optional! もunsafeだけどarray[1] や optional! はUnsafe扱いしてないっぽいですね、optional.unsafelyUnwrapped の方は対象ですが (edited)optiona!とかは安全ですよね?Unsafe はメモリアクセスの意味だと思います% cat test.swift var someInt: Int? = nil print(someInt.unsafelyUnwrapped) % swift -O test.swift 0
6 public subscript() -> T { @_transparent unsafeAddress { UnsafePointer<T>(pointer) } @_transparent nonmutating unsafeMutableAddress { pointer } }let intOnHeap = Box<Int>(0) XCTAssertEqual(intOnHeap[], 0) なるほどそのまま引数のないsubscriptになるのかextension ContiguousArray { borrowing func span() -> dependsOn(self) Span<Element> { ... construct a Span ... } } わーおrethrows とかと似ている。This document proposes a path toward an optional “strict” subset of Swift that prohibits any unsafe features. This subset is intended to be used for Swift code bases where memory safety is an absolute requirement, such as security-critical libraries.
https://forums.swift.org/t/prospective-vision-optional-strict-memory-safety-for-swift/75090subscript(unchecked:) (Spanのプロポーザルで導入予定)を使うと書いてありますね。!) とかは実行時に止まるけどチェックは付いてるからメモリ安全ではある。Optional.unsafelyUnwrapped は、! と違って、リリースビルドだとチェックも消えるらしい。ので nil の時はメモリ危険らしい。 (edited)
1
2int [3] を (Int, Int, Int) にマッピングしてるところを Vector<3, Int> にしたいらしい 互換性フラグにするとのことVector って名前についての話になっててあまり面白くなかったVector<N, T> と Vector<T, N> のどっちがいいかの話と、 略記 [N x T] は面白い
1@Test func `square returns x * x`() { #expect(square(4) == 4 * 4) } case ColorVariant { case `50` case `100` case `200` } let color = Color(hue: .red, variant: .`100`) extension UIImage { static var `10.circle`: UIImage { ... } }
https://forums.swift.org/t/se-0451-raw-identifiers/75602
import `myapp/extensions/widget/common/utils` // if explicit disambiguation is needed: `myapp/extensions/widget/common/utils`.SomeClass こんなこともできるのか (edited)
2
1modifyとreadアクセサのpitchが5年の時を経て出てきましたね。 https://forums.swift.org/t/pitch-modify-and-read-accessors/75627
1Rc<RefCell<dyn T>> 書きたくなさすぎるTaskLocal の withValue スコープ関数で包むのをやめただけisolation = #isolation なら スコープ関数自体は actor isolate されてて、 引数の operation 関数は non isolate だから、その返り値は越境している。 (edited)Task.withValue がコンパイルできていることの方がおかしいんじゃないかと思っているwithSpan から見たらそういうものが渡ってきたかどうかわからなくない?operation として直接渡されている可能性がある (edited)isolation: と同じかどうかは保証されてないよwithSpan の本文と operation の返り値の間で越境があることは変わらない@inheritsIsolation はうまくいきそう。 https://github.com/sophiapoirier/swift-evolution/blob/closure-isolation/proposals/nnnn-closure-isolation-control.md@_inheritActorContext はちょっとクセがある挙動をしているらしいTaskLocal.withValue は strict concurrency ではない Swift 5 でコンパイルされてるからスコープ関数として使えるという事みたいimport SwiftData @Model final class Item { var foo: Foo init(foo: Foo) { self.foo = foo } } struct Foo: Sendable, Codable { var value: Int? } これを item.foo.value が nil の Item を modelContext.insert(newItem) してから modelContext.save() するとクラッシュするんですが、これはバグでしょうか?@MainActor が無いだけで意味的には付いてるから、安全性と使いやすさが両立してるのは良さそう (edited)@SingleThreadMode って書く、みたいな感じなら見た目にわかるだろうけど、ファイルごとじゃなくてモジュールレベルの設定にしたいみたいだから、それだとやっぱり見た目にわからんなあ--build-system swiftbuild.class NonSendable {} func makeNonSendable() async -> NonSendable { NonSendable() } struct Demo: View { nonisolated func foo() async { _ = await makeNonSendable() } var body: some View { Text("Hello, World!") .task { _ = await makeNonSendable() // NG await foo() // OK } } } ↑これ、直接 makeNonSendable 呼び出すのと、foo 経由で呼び出すのって、ぶっちゃけ中身同じはずですが、なぜ直接 makeNonSendable を呼び出すとcross actor boundaryエラーになるのかすごい理解できないですよね… (edited)foo 経由ならやりたいことはできるのに、そうしないとできないのはなんか納得できないですよね… class NonSendable { var bool: Bool = .random() } func makeNonSendable() async -> NonSendable { NonSendable() } struct Demo: View { @State private var bool: Bool? nonisolated func foo() async { let ns = await makeNonSendable() Task { @MainActor in bool = ns.bool } } var body: some View { Text("Hello, World!") .task { // NG let ns = await makeNonSendable() bool = ns.bool } .task { // OK await foo() } } } ↑のコード、結局 foo の中でも ns がアクター隔離超えて .bool 渡してるじゃん…? (edited)
1func makeNonSendable() async -> sending NonSendable { NonSendable() } sending使えば同じになる気がしますsending 使えば普通に呼び出せますが、問題はそれ実装側が気をつけないと呼び出し側が結構大変で上のような実装をしないといけないから、ライブラリー開発する側としてはなんとかしてほしいって思いますよね…
1foo がよくて直接呼び出すのはダメかってことですねsharing とかのキーワードつけないと返せないみたいな宣言の仕方がいいじゃないかなと思っています、nonescaping から @escaping に変わったのと同じように
1sharing とかのキーワードつけないと返せないみたいな宣言の仕方がいいじゃないかなと思っています、nonescaping から @escaping に変わったのと同じように
1foo の中にやるのはよくて直接呼び出しするのはダメなのかがやはりわからない foo で呼び出せるのがおかしいです?直接呼び出せないのがおかしいです? (edited)
3
1Task { }.value で包むとタスクが別になるから Task.isCancelled な状態と区別できるのか。switch foo { case .bar where let hoge: hoge.fuga ... } みたいにswitchの中でunwrapできると嬉しいなーとずっと思ってるんだけど自分だけなんだろうか switch (foo, hoge) { case (.bar, let hoge?): hoge.fuga ... }switch foo { case let .bar(baz) where let qux = baz.qux: qux ... }